diff --git a/src/mol-model/structure/_spec/atom-set.1.spec.ts b/src/mol-model/structure/_spec/atom-set.1.spec.ts deleted file mode 100644 index 917d69bf59ff94fd416e580d9ae8aa690400eb5c..0000000000000000000000000000000000000000 --- a/src/mol-model/structure/_spec/atom-set.1.spec.ts +++ /dev/null @@ -1,193 +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 AtomSet from '../structure/atom/set.1' -import Atom from '../structure/atom' -import AtomGroup from '../structure/atom/group' - -describe('atom set', () => { - const p = (i: number, j: number) => Atom.create(i, j); - - function setToPairs(set: AtomSet): ArrayLike<Atom> { - const ret: Atom[] = []; - const it = AtomSet.atoms(set); - while (it.hasNext) { - ret[ret.length] = it.move(); - } - return ret; - } - - it('singleton pair', () => { - const set = AtomSet.ofAtoms([p(10, 11)], AtomSet.Empty); - expect(setToPairs(set)).toEqual([p(10, 11)]); - expect(AtomSet.atomHas(set, p(10, 11))).toBe(true); - expect(AtomSet.atomHas(set, p(11, 11))).toBe(false); - expect(AtomSet.atomGetAt(set, 0)).toBe(p(10, 11)); - expect(AtomSet.atomCount(set)).toBe(1); - }); - - it('multi', () => { - const gen = AtomSet.Generator(); - gen.add(1, AtomGroup.createNew(OrderedSet.ofSortedArray([4, 6, 7]))); - gen.add(3, AtomGroup.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(AtomSet.atomCount(set)).toBe(ret.length); - expect(setToPairs(set)).toEqual([p(1, 4), p(1, 6), p(1, 7), p(3, 0), p(3, 1)]); - expect(AtomSet.atomHas(set, p(10, 11))).toBe(false); - expect(AtomSet.atomHas(set, p(3, 0))).toBe(true); - expect(AtomSet.atomHas(set, p(1, 7))).toBe(true); - for (let i = 0; i < AtomSet.atomCount(set); i++) { - expect(Atom.areEqual(AtomSet.atomGetAt(set, i), ret[i])).toBe(true); - } - }); - - it('template', () => { - const template = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty) - const gen = AtomSet.TemplateGenerator(template); - gen.add(0, AtomGroup.createNew(OrderedSet.ofSortedArray([1, 2, 6]))); - gen.add(1, AtomGroup.createNew(OrderedSet.ofSingleton(3))); - const set = gen.getSet(); - - expect(AtomSet.unitGetById(set, 0)).toBe(AtomSet.unitGetById(template, 0)); - expect(AtomSet.unitGetById(set, 1)).toBe(AtomSet.unitGetById(template, 1)); - expect(set).toBe(template); - }); - - it('template 1', () => { - const template = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty) - const gen = AtomSet.TemplateGenerator(template); - gen.add(0, AtomGroup.createNew(OrderedSet.ofSortedArray([1, 2, 6]))); - gen.add(1, AtomGroup.createNew(OrderedSet.ofSingleton(4))); - const set = gen.getSet(); - - expect(AtomSet.unitGetById(set, 0)).toBe(AtomSet.unitGetById(template, 0)); - expect(AtomSet.unitGetById(set, 1) === AtomSet.unitGetById(template, 1)).toBe(false); - expect(set === template).toBe(false); - }); - - it('template union', () => { - const template = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty) - - const p13 = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const p01 = AtomSet.ofAtoms([p(0, 1)], AtomSet.Empty); - const p02 = AtomSet.ofAtoms([p(0, 2)], AtomSet.Empty); - const p06 = AtomSet.ofAtoms([p(0, 6)], AtomSet.Empty); - - const u0 = AtomSet.union([p01, p02, p06], template); - const u1 = AtomSet.union([p01, p02, p06, p13], template); - expect(AtomSet.unitGetById(u0, 0)).toBe(AtomSet.unitGetById(template, 0)); - expect(AtomSet.unitGetById(u1, 0)).toBe(AtomSet.unitGetById(template, 0)); - expect(AtomSet.unitGetById(u1, 1)).toBe(AtomSet.unitGetById(template, 1)); - expect(u1).toBe(template); - }); - - it('element at / index of', () => { - const control: Atom[] = []; - const gen = AtomSet.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, AtomGroup.createNew(OrderedSet.ofSortedArray(set))); - } - const ms = gen.getSet(); - for (let i = 0; i < control.length; i++) { - expect(Atom.areEqual(AtomSet.atomGetAt(ms, i), control[i])).toBe(true); - } - - for (let i = 0; i < control.length; i++) { - expect(AtomSet.atomIndexOf(ms, control[i])).toBe(i); - } - }); - - it('packed pairs', () => { - const set = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - expect(setToPairs(set)).toEqual([p(0, 1), p(0, 2), p(0, 6), p(1, 3)]); - }); - - it('equality', () => { - const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const b = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const c = AtomSet.ofAtoms([p(1, 3), p(0, 4), p(0, 6), p(0, 2)], AtomSet.Empty); - const d = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const e = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const f = AtomSet.ofAtoms([p(3, 3)], AtomSet.Empty); - - expect(AtomSet.areEqual(a, a)).toBe(true); - expect(AtomSet.areEqual(a, b)).toBe(true); - expect(AtomSet.areEqual(a, c)).toBe(false); - expect(AtomSet.areEqual(a, d)).toBe(false); - expect(AtomSet.areEqual(d, d)).toBe(true); - expect(AtomSet.areEqual(d, e)).toBe(true); - expect(AtomSet.areEqual(d, f)).toBe(false); - }); - - it('are intersecting', () => { - const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const b = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const c = AtomSet.ofAtoms([p(1, 3), p(0, 4), p(0, 6), p(0, 2)], AtomSet.Empty); - const d = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const e = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const f = AtomSet.ofAtoms([p(3, 3)], AtomSet.Empty); - const g = AtomSet.ofAtoms([p(10, 3), p(8, 1), p(7, 6), p(3, 2)], AtomSet.Empty); - - expect(AtomSet.areIntersecting(a, a)).toBe(true); - expect(AtomSet.areIntersecting(a, b)).toBe(true); - expect(AtomSet.areIntersecting(a, c)).toBe(true); - expect(AtomSet.areIntersecting(a, d)).toBe(true); - expect(AtomSet.areIntersecting(a, g)).toBe(false); - expect(AtomSet.areIntersecting(d, d)).toBe(true); - expect(AtomSet.areIntersecting(d, e)).toBe(true); - expect(AtomSet.areIntersecting(d, f)).toBe(false); - }); - - it('intersection', () => { - const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const b = AtomSet.ofAtoms([p(10, 3), p(0, 1), p(0, 6), p(4, 2)], AtomSet.Empty); - const c = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const d = AtomSet.ofAtoms([p(2, 3)], AtomSet.Empty); - expect(AtomSet.intersect(a, a)).toBe(a); - expect(setToPairs(AtomSet.intersect(a, b))).toEqual([p(0, 1), p(0, 6)]); - expect(setToPairs(AtomSet.intersect(a, c))).toEqual([p(1, 3)]); - expect(setToPairs(AtomSet.intersect(c, d))).toEqual([]); - }); - - it('subtract', () => { - const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const a1 = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); - const b = AtomSet.ofAtoms([p(10, 3), p(0, 1), p(0, 6), p(4, 2)], AtomSet.Empty); - const c = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const d = AtomSet.ofAtoms([p(2, 3)], AtomSet.Empty); - const e = AtomSet.ofAtoms([p(0, 2)], AtomSet.Empty); - expect(setToPairs(AtomSet.subtract(a, a))).toEqual([]); - expect(setToPairs(AtomSet.subtract(a, a1))).toEqual([]); - expect(setToPairs(AtomSet.subtract(a, b))).toEqual([p(0, 2), p(1, 3)]); - expect(setToPairs(AtomSet.subtract(c, d))).toEqual([p(1, 3)]); - expect(setToPairs(AtomSet.subtract(a, c))).toEqual([p(0, 1), p(0, 2), p(0, 6)]); - expect(setToPairs(AtomSet.subtract(c, a))).toEqual([]); - expect(setToPairs(AtomSet.subtract(d, a))).toEqual([p(2, 3)]); - expect(setToPairs(AtomSet.subtract(a, e))).toEqual([p(0, 1), p(0, 6), p(1, 3)]); - }); - - it('union', () => { - const a = AtomSet.ofAtoms([p(1, 3), p(0, 1)], AtomSet.Empty); - const a1 = AtomSet.ofAtoms([p(1, 3), p(0, 1)], AtomSet.Empty); - const b = AtomSet.ofAtoms([p(10, 3), p(0, 1)], AtomSet.Empty); - const c = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); - const d = AtomSet.ofAtoms([p(2, 3)], AtomSet.Empty); - expect(AtomSet.union([a], AtomSet.Empty)).toBe(a); - expect(AtomSet.union([a, a], AtomSet.Empty)).toBe(a); - expect(setToPairs(AtomSet.union([a, a], AtomSet.Empty))).toEqual([p(0, 1), p(1, 3)]); - expect(setToPairs(AtomSet.union([a, a1], AtomSet.Empty))).toEqual([p(0, 1), p(1, 3)]); - expect(setToPairs(AtomSet.union([a, b], AtomSet.Empty))).toEqual([p(0, 1), p(1, 3), p(10, 3)]); - expect(setToPairs(AtomSet.union([c, d], AtomSet.Empty))).toEqual([p(1, 3), p(2, 3)]); - expect(setToPairs(AtomSet.union([a, b, c, d], AtomSet.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/_spec/atom-set.spec.ts b/src/mol-model/structure/_spec/atom-set.spec.ts index c62081f34ceb3fc82ae10dbd28ade861db416fbb..5489a9a4fb0afbd8381a024b03f5a673de253dcd 100644 --- a/src/mol-model/structure/_spec/atom-set.spec.ts +++ b/src/mol-model/structure/_spec/atom-set.spec.ts @@ -7,6 +7,7 @@ import { OrderedSet } from 'mol-data/int' import AtomSet from '../structure/atom/set' import Atom from '../structure/atom' +import AtomGroup from '../structure/atom/group' describe('atom set', () => { const p = (i: number, j: number) => Atom.create(i, j); @@ -21,7 +22,7 @@ describe('atom set', () => { } it('singleton pair', () => { - const set = AtomSet.create(p(10, 11)); + const set = AtomSet.ofAtoms([p(10, 11)], AtomSet.Empty); expect(setToPairs(set)).toEqual([p(10, 11)]); expect(AtomSet.atomHas(set, p(10, 11))).toBe(true); expect(AtomSet.atomHas(set, p(11, 11))).toBe(false); @@ -29,15 +30,10 @@ describe('atom set', () => { expect(AtomSet.atomCount(set)).toBe(1); }); - it('singleton number', () => { - const set = AtomSet.create(p(10, 11)); - expect(setToPairs(set)).toEqual([p(10, 11)]); - }); - it('multi', () => { const gen = AtomSet.Generator(); - gen.add(1, OrderedSet.ofSortedArray([4, 6, 7])); - gen.add(3, OrderedSet.ofRange(0, 1)); + gen.add(1, AtomGroup.createNew(OrderedSet.ofSortedArray([4, 6, 7]))); + gen.add(3, AtomGroup.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(AtomSet.atomCount(set)).toBe(ret.length); @@ -50,6 +46,46 @@ describe('atom set', () => { } }); + it('template', () => { + const template = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty) + const gen = AtomSet.TemplateGenerator(template); + gen.add(0, AtomGroup.createNew(OrderedSet.ofSortedArray([1, 2, 6]))); + gen.add(1, AtomGroup.createNew(OrderedSet.ofSingleton(3))); + const set = gen.getSet(); + + expect(AtomSet.unitGetById(set, 0)).toBe(AtomSet.unitGetById(template, 0)); + expect(AtomSet.unitGetById(set, 1)).toBe(AtomSet.unitGetById(template, 1)); + expect(set).toBe(template); + }); + + it('template 1', () => { + const template = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty) + const gen = AtomSet.TemplateGenerator(template); + gen.add(0, AtomGroup.createNew(OrderedSet.ofSortedArray([1, 2, 6]))); + gen.add(1, AtomGroup.createNew(OrderedSet.ofSingleton(4))); + const set = gen.getSet(); + + expect(AtomSet.unitGetById(set, 0)).toBe(AtomSet.unitGetById(template, 0)); + expect(AtomSet.unitGetById(set, 1) === AtomSet.unitGetById(template, 1)).toBe(false); + expect(set === template).toBe(false); + }); + + it('template union', () => { + const template = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty) + + const p13 = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const p01 = AtomSet.ofAtoms([p(0, 1)], AtomSet.Empty); + const p02 = AtomSet.ofAtoms([p(0, 2)], AtomSet.Empty); + const p06 = AtomSet.ofAtoms([p(0, 6)], AtomSet.Empty); + + const u0 = AtomSet.union([p01, p02, p06], template); + const u1 = AtomSet.union([p01, p02, p06, p13], template); + expect(AtomSet.unitGetById(u0, 0)).toBe(AtomSet.unitGetById(template, 0)); + expect(AtomSet.unitGetById(u1, 0)).toBe(AtomSet.unitGetById(template, 0)); + expect(AtomSet.unitGetById(u1, 1)).toBe(AtomSet.unitGetById(template, 1)); + expect(u1).toBe(template); + }); + it('element at / index of', () => { const control: Atom[] = []; const gen = AtomSet.Generator(); @@ -59,7 +95,7 @@ describe('atom set', () => { control[control.length] = p(i * i, j * j + 1); set[set.length] = j * j + 1; } - gen.add(i * i, OrderedSet.ofSortedArray(set)); + gen.add(i * i, AtomGroup.createNew(OrderedSet.ofSortedArray(set))); } const ms = gen.getSet(); for (let i = 0; i < control.length; i++) { @@ -72,17 +108,17 @@ describe('atom set', () => { }); it('packed pairs', () => { - const set = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); + const set = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); expect(setToPairs(set)).toEqual([p(0, 1), p(0, 2), p(0, 6), p(1, 3)]); }); it('equality', () => { - const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const b = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const c = AtomSet.create([p(1, 3), p(0, 4), p(0, 6), p(0, 2)]); - const d = AtomSet.create([p(1, 3)]); - const e = AtomSet.create([p(1, 3)]); - const f = AtomSet.create([p(3, 3)]); + const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const b = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const c = AtomSet.ofAtoms([p(1, 3), p(0, 4), p(0, 6), p(0, 2)], AtomSet.Empty); + const d = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const e = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const f = AtomSet.ofAtoms([p(3, 3)], AtomSet.Empty); expect(AtomSet.areEqual(a, a)).toBe(true); expect(AtomSet.areEqual(a, b)).toBe(true); @@ -94,13 +130,13 @@ describe('atom set', () => { }); it('are intersecting', () => { - const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const b = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const c = AtomSet.create([p(1, 3), p(0, 4), p(0, 6), p(0, 2)]); - const d = AtomSet.create([p(1, 3)]); - const e = AtomSet.create([p(1, 3)]); - const f = AtomSet.create([p(3, 3)]); - const g = AtomSet.create([p(10, 3), p(8, 1), p(7, 6), p(3, 2)]); + const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const b = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const c = AtomSet.ofAtoms([p(1, 3), p(0, 4), p(0, 6), p(0, 2)], AtomSet.Empty); + const d = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const e = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const f = AtomSet.ofAtoms([p(3, 3)], AtomSet.Empty); + const g = AtomSet.ofAtoms([p(10, 3), p(8, 1), p(7, 6), p(3, 2)], AtomSet.Empty); expect(AtomSet.areIntersecting(a, a)).toBe(true); expect(AtomSet.areIntersecting(a, b)).toBe(true); @@ -113,10 +149,10 @@ describe('atom set', () => { }); it('intersection', () => { - const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]); - const c = AtomSet.create([p(1, 3)]); - const d = AtomSet.create([p(2, 3)]); + const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const b = AtomSet.ofAtoms([p(10, 3), p(0, 1), p(0, 6), p(4, 2)], AtomSet.Empty); + const c = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const d = AtomSet.ofAtoms([p(2, 3)], AtomSet.Empty); expect(AtomSet.intersect(a, a)).toBe(a); expect(setToPairs(AtomSet.intersect(a, b))).toEqual([p(0, 1), p(0, 6)]); expect(setToPairs(AtomSet.intersect(a, c))).toEqual([p(1, 3)]); @@ -124,12 +160,12 @@ describe('atom set', () => { }); it('subtract', () => { - const a = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const a1 = AtomSet.create([p(1, 3), p(0, 1), p(0, 6), p(0, 2)]); - const b = AtomSet.create([p(10, 3), p(0, 1), p(0, 6), p(4, 2)]); - const c = AtomSet.create([p(1, 3)]); - const d = AtomSet.create([p(2, 3)]); - const e = AtomSet.create([p(0, 2)]); + const a = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const a1 = AtomSet.ofAtoms([p(1, 3), p(0, 1), p(0, 6), p(0, 2)], AtomSet.Empty); + const b = AtomSet.ofAtoms([p(10, 3), p(0, 1), p(0, 6), p(4, 2)], AtomSet.Empty); + const c = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const d = AtomSet.ofAtoms([p(2, 3)], AtomSet.Empty); + const e = AtomSet.ofAtoms([p(0, 2)], AtomSet.Empty); expect(setToPairs(AtomSet.subtract(a, a))).toEqual([]); expect(setToPairs(AtomSet.subtract(a, a1))).toEqual([]); expect(setToPairs(AtomSet.subtract(a, b))).toEqual([p(0, 2), p(1, 3)]); @@ -141,17 +177,17 @@ describe('atom set', () => { }); it('union', () => { - const a = AtomSet.create([p(1, 3), p(0, 1)]); - const a1 = AtomSet.create([p(1, 3), p(0, 1)]); - const b = AtomSet.create([p(10, 3), p(0, 1)]); - const c = AtomSet.create([p(1, 3)]); - const d = AtomSet.create([p(2, 3)]); - expect(AtomSet.unionMany([a])).toBe(a); - expect(AtomSet.union(a, a)).toBe(a); - expect(setToPairs(AtomSet.union(a, a))).toEqual([p(0, 1), p(1, 3)]); - expect(setToPairs(AtomSet.union(a, a1))).toEqual([p(0, 1), p(1, 3)]); - expect(setToPairs(AtomSet.union(a, b))).toEqual([p(0, 1), p(1, 3), p(10, 3)]); - expect(setToPairs(AtomSet.union(c, d))).toEqual([p(1, 3), p(2, 3)]); - expect(setToPairs(AtomSet.unionMany([a, b, c, d]))).toEqual([p(0, 1), p(1, 3), p(2, 3), p(10, 3)]); + const a = AtomSet.ofAtoms([p(1, 3), p(0, 1)], AtomSet.Empty); + const a1 = AtomSet.ofAtoms([p(1, 3), p(0, 1)], AtomSet.Empty); + const b = AtomSet.ofAtoms([p(10, 3), p(0, 1)], AtomSet.Empty); + const c = AtomSet.ofAtoms([p(1, 3)], AtomSet.Empty); + const d = AtomSet.ofAtoms([p(2, 3)], AtomSet.Empty); + expect(AtomSet.union([a], AtomSet.Empty)).toBe(a); + expect(AtomSet.union([a, a], AtomSet.Empty)).toBe(a); + expect(setToPairs(AtomSet.union([a, a], AtomSet.Empty))).toEqual([p(0, 1), p(1, 3)]); + expect(setToPairs(AtomSet.union([a, a1], AtomSet.Empty))).toEqual([p(0, 1), p(1, 3)]); + expect(setToPairs(AtomSet.union([a, b], AtomSet.Empty))).toEqual([p(0, 1), p(1, 3), p(10, 3)]); + expect(setToPairs(AtomSet.union([c, d], AtomSet.Empty))).toEqual([p(1, 3), p(2, 3)]); + expect(setToPairs(AtomSet.union([a, b, c, d], AtomSet.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/query/generators.ts b/src/mol-model/structure/query/generators.ts index 305488104df685fbfc81f9779cc712d0970e7bc2..024400c963aac77ecb05bdff04fa4f2b8e083781 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -46,7 +46,7 @@ function atomGroupsLinear(atomTest: Atom.Predicate): Query { for (let i = 0, _i = unitIds.length; i < _i; i++) { const unitId = unitIds[i]; l.unit = units[unitId]; - const set = AtomSet.unitGetByIndex(atoms, i); + const set = AtomSet.unitGetByIndex(atoms, i).atoms; builder.beginUnit(); for (let j = 0, _j = OrderedSet.size(set); j < _j; j++) { @@ -71,7 +71,7 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A const unitId = unitIds[i]; const unit = units[unitId]; l.unit = unit; - const set = AtomSet.unitGetByIndex(atoms, i); + const set = AtomSet.unitGetByIndex(atoms, i).atoms; builder.beginUnit(); const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); @@ -129,7 +129,7 @@ class LinearGroupingBuilder { for (let i = 0, _i = this.builders.length; i < _i; i++) { atoms[i] = this.builders[i].singleton(); } - return Structure.create(this.structure.units, AtomSet.create(atoms)); + return Structure.create(this.structure.units, AtomSet.ofAtoms(atoms, this.structure.atoms)); } private fullSelection() { @@ -162,7 +162,7 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group const unitId = unitIds[i]; const unit = units[unitId]; l.unit = unit; - const set = AtomSet.unitGetByIndex(atoms, i); + const set = AtomSet.unitGetByIndex(atoms, i).atoms; const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); diff --git a/src/mol-model/structure/query/selection.ts b/src/mol-model/structure/query/selection.ts index 3f568b501a55d72bdf4d17bca5e2a2789e2a3552..1bb2b5776f74e3a719f0e6fe98b7414e2974751f 100644 --- a/src/mol-model/structure/query/selection.ts +++ b/src/mol-model/structure/query/selection.ts @@ -38,7 +38,7 @@ namespace Selection { if (!sel.length) return Structure.Empty; const sets = []; for (let i = 0, _i = sel.length; i < _i; i++) sets[sets.length] = sel[i].atoms; - return Structure.create(unionUnits(sel), AtomSet.unionMany(sets)); + return Structure.create(unionUnits(sel), AtomSet.union(sets, AtomSet.Empty)); } export function structures(sel: Selection): Iterator<Structure> { diff --git a/src/mol-model/structure/structure/atom/group.ts b/src/mol-model/structure/structure/atom/group.ts index 575ed431f1e0852b0fe5687679cc4a13a1e03ced..22421f10e3794b757e0f583c3f860cdaa0e67a97 100644 --- a/src/mol-model/structure/structure/atom/group.ts +++ b/src/mol-model/structure/structure/atom/group.ts @@ -28,6 +28,11 @@ namespace AtomGroup { return createNew(atoms); } + export function createChild(parent: AtomGroup, atoms: OrderedSet): AtomGroup { + if (OrderedSet.areEqual(atoms, parent.atoms)) return parent; + return createNew(atoms); + } + export function size(group: AtomGroup) { return OrderedSet.size(group.atoms); } export function has(group: AtomGroup, atom: number) { return OrderedSet.has(group.atoms, atom); } export function getAt(group: AtomGroup, i: number) { return OrderedSet.getAt(group.atoms, i); } diff --git a/src/mol-model/structure/structure/atom/impl/builder.ts b/src/mol-model/structure/structure/atom/impl/builder.ts index cf9d2b8be70a5429c293b226da4eb723499e7a7a..ca98139142b508c2f0df5a2ecb203d5ba591ac5e 100644 --- a/src/mol-model/structure/structure/atom/impl/builder.ts +++ b/src/mol-model/structure/structure/atom/impl/builder.ts @@ -6,6 +6,7 @@ import AtomSet from '../set' import Atom from '../../atom' +import AtomGroup from '../group' import { OrderedSet, IntMap } from 'mol-data/int' import { sortArray } from 'mol-data/util/sort' @@ -45,14 +46,10 @@ export class Builder { const l = unit.length; if (!this.sorted && l > 1) sortArray(unit); - const set = OrderedSet.ofSortedArray(unit); - const parentSet = AtomSet.unitGetById(this.parent, k); - if (OrderedSet.areEqual(set, parentSet)) { - generator.add(k, parentSet); - } else { - generator.add(k, set); - allEqual = false; - } + const parentGroup = AtomSet.unitGetById(this.parent, k); + const group = AtomGroup.createChild(parentGroup, OrderedSet.ofSortedArray(unit)); + if (group !== parentGroup) allEqual = false; + generator.add(k, group); } return allEqual ? this.parent : generator.getSet(); } diff --git a/src/mol-model/structure/structure/atom/impl/set.1.ts b/src/mol-model/structure/structure/atom/impl/set.1.ts deleted file mode 100644 index 34ec544afa3b1005a02b0cbeb592cdacf5eadd5d..0000000000000000000000000000000000000000 --- a/src/mol-model/structure/structure/atom/impl/set.1.ts +++ /dev/null @@ -1,426 +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 Atom from '../../atom' -import AtomGroup from '../group' - -/** Long and painful implementation starts here */ - -export type AtomSetImpl = { groups: IntMap<AtomGroup>, offsets: Int32Array, hashCode: number, keys: SortedArray } - -export const Empty: AtomSetImpl = { groups: IntMap.Empty, offsets: new Int32Array(1), hashCode: 0, keys: SortedArray.Empty }; - -export function ofAtoms(atoms: ArrayLike<Atom>, template: AtomSetImpl): AtomSetImpl { - return ofAtomsImpl(atoms, template); -} - -export function getKeys(set: AtomSetImpl): SortedArray { - return set.keys; -} - -export function keyCount(set: AtomSetImpl): number { - return set.keys.length; -} - -export function hasKey(set: AtomSetImpl, key: number): boolean { - return set.groups.has(key); -} - -export function getKey(set: AtomSetImpl, index: number): number { - return set.keys[index]; -} - -export function hasAtom(set: AtomSetImpl, t: Atom): boolean { - const os = set.groups.get(Atom.unit(t)); - return !!os && AtomGroup.has(os, Atom.index(t)); -} - -export function getByKey(set: AtomSetImpl, key: number): AtomGroup { - return set.groups.get(key) || AtomGroup.Empty; -} - -export function getByIndex(set: AtomSetImpl, index: number): AtomGroup { - const key = set.keys[index]; - return set.groups.get(key) || AtomGroup.Empty; -} - -export function getAt(set: AtomSetImpl, i: number): Atom { - const { offsets, keys } = set; - const o = getOffsetIndex(offsets, i); - if (o >= offsets.length - 1) return Atom.Zero; - const k = keys[o]; - const e = AtomGroup.getAt(set.groups.get(k), i - offsets[o]); - return Atom.create(k, e); -} - -export function indexOf(set: AtomSetImpl, t: Atom) { - const { keys } = set; - const u = Atom.unit(t); - const setIdx = SortedArray.indexOf(keys, u); - if (setIdx < 0) return -1; - const o = AtomGroup.indexOf(set.groups.get(u), Atom.index(t)); - if (o < 0) return -1; - return set.offsets[setIdx] + o; -} - -/** Number elements in the "child" sets */ -export function size(set: AtomSetImpl) { - return set.offsets[set.offsets.length - 1]; -} - -export function hashCode(set: AtomSetImpl) { - if (set.hashCode !== -1) return set.hashCode; - return computeHash(set); -} - -export function areEqual(a: AtomSetImpl, b: AtomSetImpl) { - 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 (!AtomGroup.areEqual(aG.get(k), bG.get(k))) return false; - } - return true; -} - -export function areIntersecting(a: AtomSetImpl, b: AtomSetImpl) { - 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.atoms, bk.atoms)) return true; - } - return false; -} - -export function intersect(a: AtomSetImpl, b: AtomSetImpl) { - 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, AtomGroup.intersect(aG.get(k), bk), ak, bk); - } - return generator.getSet(); -} - -export function subtract(a: AtomSetImpl, b: AtomSetImpl) { - 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 = AtomGroup.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<AtomSetImpl>, template: AtomSetImpl) { - return findUnion(sets, template); -} - -class ElementsIterator implements Iterator<Atom> { - 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 Atom.Zero; - const ret = Atom.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).atoms; - this.currentIndex = 0; - this.currentSize = OS.size(this.currentSet); - return true; - } - - constructor(private elements: AtomSetImpl) { - this.keyCount = elements.keys.length; - this.hasNext = this.keyCount > 0; - this.advance(); - } -} - -export function values(set: AtomSetImpl): Iterator<Atom> { - return new ElementsIterator(set); -} - -export class TemplateAtomSetGenerator { - private keys: number[] = []; - private groups = IntMap.Mutable<AtomGroup>(); - private templateGroups: IntMap<AtomGroup>; - private equalGroups = 0; - - add(unit: number, group: AtomGroup) { - if (AtomGroup.size(group) === 0) return; - this.keys[this.keys.length] = unit; - let t: AtomGroup; - if (this.templateGroups.has(unit) && AtomGroup.areEqual(t = this.templateGroups.get(unit), group)) { - this.groups.set(unit, t); - this.equalGroups++; - } else { - this.groups.set(unit, group); - } - } - - getSet(): AtomSetImpl { - if (this.equalGroups === this.template.keys.length && this.equalGroups === this.keys.length) { - return this.template; - } - return create(this.keys, this.groups); - } - - constructor(private template: AtomSetImpl) { - this.templateGroups = template.groups; - } -} - -export function TemplateGenerator(template: AtomSetImpl) { - return new TemplateAtomSetGenerator(template); -} - -export class AtomSetGenerator { - private keys: number[] = []; - private groups = IntMap.Mutable<AtomGroup>(); - - add(unit: number, group: AtomGroup) { - if (AtomGroup.size(group) === 0) return; - this.keys[this.keys.length] = unit; - this.groups.set(unit, group); - } - - getSet(): AtomSetImpl { - 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<AtomGroup>(); - private aEqual = 0; - private bEqual = 0; - - add(unit: number, group: AtomGroup, a: AtomGroup, b: AtomGroup) { - if (AtomGroup.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: AtomGroup, a: AtomGroup) { - if (AtomGroup.size(group) === 0) return; - - if (a === group) this.aEqual++; - this.keys[this.keys.length] = unit; - this.groups.set(unit, group); - } - - constructor(private a: AtomSetImpl, private b: AtomSetImpl) { - } - - getSet(): AtomSetImpl { - 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<AtomGroup>): AtomSetImpl { - sortArray(keys); - let runningSize = 0; - const offsets = new Int32Array(keys.length + 1); - for (let i = 0, _i = keys.length; i < _i; i++) { - runningSize += AtomGroup.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 ofAtomsImpl(xs: ArrayLike<Atom>, template: AtomSetImpl) { - 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 = Atom.unit(x), v = Atom.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]; - const group = AtomGroup.createNew(OS.ofSortedArray(normalizeArray(elements.get(k)))); - generator.add(k, group); - } - - return generator.getSet(); -} - -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: AtomSetImpl) { - 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 + AtomGroup.hashCode(groups.get(k))) | 0; - } - hash = (31 * hash + size(set)) | 0; - hash = hash1(hash); - set.hashCode = hash; - return hash; -} - -function findUnion(sets: ArrayLike<AtomSetImpl>, template: AtomSetImpl) { - 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<AtomGroup>(); - 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<AtomGroup>, template: AtomSetImpl) { - let equalCount = 0; - let tg = template.groups, a: AtomGroup, t: AtomGroup; - for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - if (tg.has(k) && AtomGroup.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<AtomGroup>, a: AtomSetImpl) { - 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, AtomGroup.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/atom/impl/set.ts b/src/mol-model/structure/structure/atom/impl/set.ts index c63d3e4598508c43f93573a05bfdd2cad0f7628f..34ec544afa3b1005a02b0cbeb592cdacf5eadd5d 100644 --- a/src/mol-model/structure/structure/atom/impl/set.ts +++ b/src/mol-model/structure/structure/atom/impl/set.ts @@ -4,126 +4,169 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { SortedArray, Interval, Iterator, OrderedSet, IntMap } from 'mol-data/int' +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 Atom from '../../atom' +import AtomGroup from '../group' /** Long and painful implementation starts here */ -export interface AtomSetElements { sets: IntMap<OrderedSet>, offsets: Int32Array, hashCode: number, keys: SortedArray } -export type AtomSetImpl = Atom | AtomSetElements +export type AtomSetImpl = { groups: IntMap<AtomGroup>, offsets: Int32Array, hashCode: number, keys: SortedArray } -export const Empty: AtomSetImpl = { sets: IntMap.Empty, offsets: new Int32Array(1), hashCode: 0, keys: SortedArray.Empty }; +export const Empty: AtomSetImpl = { groups: IntMap.Empty, offsets: new Int32Array(1), hashCode: 0, keys: SortedArray.Empty }; -export function create(data: Atom | ArrayLike<Atom>): AtomSetImpl { - if (Atom.is(data)) return data; - return ofAtoms(data); +export function ofAtoms(atoms: ArrayLike<Atom>, template: AtomSetImpl): AtomSetImpl { + return ofAtomsImpl(atoms, template); } export function getKeys(set: AtomSetImpl): SortedArray { - if (typeof set === 'number') return SortedArray.ofSingleton(set); - return (set as AtomSetElements).keys; + return set.keys; } export function keyCount(set: AtomSetImpl): number { - if (typeof set === 'number') return 1; - return (set as AtomSetElements).keys.length; + return set.keys.length; } export function hasKey(set: AtomSetImpl, key: number): boolean { - if (typeof set === 'number') return Atom.unit(set) === key; - return !!(set as AtomSetElements).sets.has(key); + return set.groups.has(key); } export function getKey(set: AtomSetImpl, index: number): number { - if (typeof set === 'number') return Atom.unit(set); - return (set as AtomSetElements).keys[index]; + return set.keys[index]; } export function hasAtom(set: AtomSetImpl, t: Atom): boolean { - if (typeof set === 'number') return Atom.areEqual(t, set); - const os = (set as AtomSetElements).sets.get(Atom.unit(t)); - return !!os && OrderedSet.has(os, Atom.index(t)); + const os = set.groups.get(Atom.unit(t)); + return !!os && AtomGroup.has(os, Atom.index(t)); } -export function getByKey(set: AtomSetImpl, key: number): OrderedSet { - if (typeof set === 'number') { - return Atom.unit(set) === key ? OrderedSet.ofSingleton(Atom.index(set)) : OrderedSet.Empty; - } - return (set as AtomSetElements).sets.get(key) || OrderedSet.Empty; +export function getByKey(set: AtomSetImpl, key: number): AtomGroup { + return set.groups.get(key) || AtomGroup.Empty; } -export function getByIndex(set: AtomSetImpl, index: number): OrderedSet { - if (typeof set === 'number') return index === 0 ? OrderedSet.ofSingleton(Atom.index(set)) : OrderedSet.Empty; - const key = (set as AtomSetElements).keys[index]; - return (set as AtomSetElements).sets.get(key) || OrderedSet.Empty; +export function getByIndex(set: AtomSetImpl, index: number): AtomGroup { + const key = set.keys[index]; + return set.groups.get(key) || AtomGroup.Empty; } export function getAt(set: AtomSetImpl, i: number): Atom { - if (typeof set === 'number') return set; - return getAtE(set as AtomSetElements, i); + const { offsets, keys } = set; + const o = getOffsetIndex(offsets, i); + if (o >= offsets.length - 1) return Atom.Zero; + const k = keys[o]; + const e = AtomGroup.getAt(set.groups.get(k), i - offsets[o]); + return Atom.create(k, e); } export function indexOf(set: AtomSetImpl, t: Atom) { - if (typeof set === 'number') return Atom.areEqual(set, t) ? 0 : -1; - return indexOfE(set as AtomSetElements, t); + const { keys } = set; + const u = Atom.unit(t); + const setIdx = SortedArray.indexOf(keys, u); + if (setIdx < 0) return -1; + const o = AtomGroup.indexOf(set.groups.get(u), Atom.index(t)); + if (o < 0) return -1; + return set.offsets[setIdx] + o; } /** Number elements in the "child" sets */ export function size(set: AtomSetImpl) { - if (typeof set === 'number') return 1; - return (set as AtomSetElements).offsets[(set as AtomSetElements).offsets.length - 1]; + return set.offsets[set.offsets.length - 1]; } export function hashCode(set: AtomSetImpl) { - if (typeof set === 'number') return Atom.hashCode(set); - if ((set as AtomSetElements).hashCode !== -1) return (set as AtomSetElements).hashCode; - return computeHash((set as AtomSetElements)); + if (set.hashCode !== -1) return set.hashCode; + return computeHash(set); } export function areEqual(a: AtomSetImpl, b: AtomSetImpl) { - if (typeof a === 'number') { - if (typeof b === 'number') return Atom.areEqual(a, b); - return false; + 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 (!AtomGroup.areEqual(aG.get(k), bG.get(k))) return false; } - if (typeof b === 'number') return false; - return areEqualEE(a as AtomSetElements, b as AtomSetElements); + return true; } export function areIntersecting(a: AtomSetImpl, b: AtomSetImpl) { - if (typeof a === 'number') { - if (typeof b === 'number') return Atom.areEqual(a, b); - return areIntersectingNE(a, b as AtomSetElements); + 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.atoms, bk.atoms)) return true; } - if (typeof b === 'number') return areIntersectingNE(b, a as AtomSetElements); - return areIntersectingEE(a as AtomSetElements, b as AtomSetElements); + return false; } export function intersect(a: AtomSetImpl, b: AtomSetImpl) { - if (typeof a === 'number') { - if (typeof b === 'number') return Atom.areEqual(a, b) ? a : Empty; - return intersectNE(a, b as AtomSetElements); + 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, AtomGroup.intersect(aG.get(k), bk), ak, bk); } - if (typeof b === 'number') return intersectNE(b, a as AtomSetElements); - return intersectEE(a as AtomSetElements, b as AtomSetElements); + return generator.getSet(); } export function subtract(a: AtomSetImpl, b: AtomSetImpl) { - if (typeof a === 'number') { - if (typeof b === 'number') return Atom.areEqual(a, b) ? Empty : a; - return subtractNE(a, b as AtomSetElements); - } - if (typeof b === 'number') return subtractEN(a as AtomSetElements, b); - return subtractEE(a as AtomSetElements, b as AtomSetElements); -} + 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); -export function union(a: AtomSetImpl, b: AtomSetImpl) { - return findUnion([a, b]); + 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 = AtomGroup.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<AtomSetImpl>) { - return findUnion(sets); +export function unionMany(sets: ArrayLike<AtomSetImpl>, template: AtomSetImpl) { + return findUnion(sets, template); } class ElementsIterator implements Iterator<Atom> { @@ -132,13 +175,13 @@ class ElementsIterator implements Iterator<Atom> { private setIndex = -1; private currentIndex = 0; private currentSize = 0; - private currentSet: OrderedSet = OrderedSet.Empty; + private currentSet: OS = OS.Empty; hasNext: boolean = false; move() { if (!this.hasNext) return Atom.Zero; - const ret = Atom.create(this.unit, OrderedSet.getAt(this.currentSet, this.currentIndex++)); + const ret = Atom.create(this.unit, OS.getAt(this.currentSet, this.currentIndex++)); if (this.currentIndex >= this.currentSize) this.advance(); return ret; } @@ -149,13 +192,13 @@ class ElementsIterator implements Iterator<Atom> { return false; } this.unit = this.elements.keys[this.setIndex]; - this.currentSet = this.elements.sets.get(this.unit); + this.currentSet = this.elements.groups.get(this.unit).atoms; this.currentIndex = 0; - this.currentSize = OrderedSet.size(this.currentSet); + this.currentSize = OS.size(this.currentSet); return true; } - constructor(private elements: AtomSetElements) { + constructor(private elements: AtomSetImpl) { this.keyCount = elements.keys.length; this.hasNext = this.keyCount > 0; this.advance(); @@ -163,33 +206,55 @@ class ElementsIterator implements Iterator<Atom> { } export function values(set: AtomSetImpl): Iterator<Atom> { - if (typeof set === 'number') return Iterator.Value(set as Atom); - return new ElementsIterator(set as AtomSetElements); + return new ElementsIterator(set); } -export class AtomSetGenerator { +export class TemplateAtomSetGenerator { private keys: number[] = []; - private sets = IntMap.Mutable<OrderedSet>(); + private groups = IntMap.Mutable<AtomGroup>(); + private templateGroups: IntMap<AtomGroup>; + private equalGroups = 0; - add(unit: number, set: OrderedSet) { - if (OrderedSet.size(set) === 0) return; + add(unit: number, group: AtomGroup) { + if (AtomGroup.size(group) === 0) return; this.keys[this.keys.length] = unit; - this.sets.set(unit, set); + let t: AtomGroup; + if (this.templateGroups.has(unit) && AtomGroup.areEqual(t = this.templateGroups.get(unit), group)) { + this.groups.set(unit, t); + this.equalGroups++; + } else { + this.groups.set(unit, group); + } } - addUnion(unit: number, set: OrderedSet) { - if (OrderedSet.size(set) === 0) return; - - if (this.sets.has(unit)) { - this.sets.set(unit, OrderedSet.union(this.sets.get(unit), set)); - } else { - this.keys[this.keys.length] = unit; - this.sets.set(unit, set); + getSet(): AtomSetImpl { + if (this.equalGroups === this.template.keys.length && this.equalGroups === this.keys.length) { + return this.template; } + return create(this.keys, this.groups); + } + + constructor(private template: AtomSetImpl) { + this.templateGroups = template.groups; + } +} + +export function TemplateGenerator(template: AtomSetImpl) { + return new TemplateAtomSetGenerator(template); +} + +export class AtomSetGenerator { + private keys: number[] = []; + private groups = IntMap.Mutable<AtomGroup>(); + + add(unit: number, group: AtomGroup) { + if (AtomGroup.size(group) === 0) return; + this.keys[this.keys.length] = unit; + this.groups.set(unit, group); } getSet(): AtomSetImpl { - return ofKeysAndSets(this.keys, this.sets); + return create(this.keys, this.groups); } } @@ -197,24 +262,48 @@ export function Generator() { return new AtomSetGenerator(); } -function ofKeysAndSetsElemements(keys: number[], sets: IntMap<OrderedSet>): AtomSetElements { +/** 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<AtomGroup>(); + private aEqual = 0; + private bEqual = 0; + + add(unit: number, group: AtomGroup, a: AtomGroup, b: AtomGroup) { + if (AtomGroup.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: AtomGroup, a: AtomGroup) { + if (AtomGroup.size(group) === 0) return; + + if (a === group) this.aEqual++; + this.keys[this.keys.length] = unit; + this.groups.set(unit, group); + } + + constructor(private a: AtomSetImpl, private b: AtomSetImpl) { + } + + getSet(): AtomSetImpl { + 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<AtomGroup>): AtomSetImpl { sortArray(keys); let runningSize = 0; const offsets = new Int32Array(keys.length + 1); for (let i = 0, _i = keys.length; i < _i; i++) { - runningSize += OrderedSet.size(sets.get(keys[i])); + runningSize += AtomGroup.size(groups.get(keys[i])); offsets[i + 1] = runningSize; } - return { keys: SortedArray.ofSortedArray(keys), sets: IntMap.asImmutable(sets), offsets, hashCode: -1 }; -} - - -function ofKeysAndSets(keys: number[], sets: IntMap<OrderedSet>) { - if (keys.length === 1) { - const set = sets.get(keys[0]); - if (OrderedSet.size(set) === 1) return Atom.create(keys[0], OrderedSet.getAt(set, 0)); - } - return ofKeysAndSetsElemements(keys, sets); + return { keys: SortedArray.ofSortedArray(keys), groups: IntMap.asImmutable(groups), offsets, hashCode: -1 }; } function getUniqueElements(xs: number[]): number[] { @@ -239,7 +328,7 @@ function normalizeArray(xs: number[]): number[] { return xs; } -function ofAtoms(xs: ArrayLike<Atom>) { +function ofAtomsImpl(xs: ArrayLike<Atom>, template: AtomSetImpl) { if (xs.length === 0) return Empty; const elements = IntMap.Mutable<number[]>(); @@ -256,13 +345,14 @@ function ofAtoms(xs: ArrayLike<Atom>) { } } - const sets = IntMap.Mutable<OrderedSet>(); + const generator = TemplateGenerator(template); for (let i = 0, _i = keys.length; i < _i; i++) { const k = keys[i]; - sets.set(k, OrderedSet.ofSortedArray(normalizeArray(elements.get(k)))); + const group = AtomGroup.createNew(OS.ofSortedArray(normalizeArray(elements.get(k)))); + generator.add(k, group); } - return ofKeysAndSets(keys, sets); + return generator.getSet(); } function getOffsetIndex(xs: ArrayLike<number>, value: number) { @@ -280,32 +370,13 @@ function getOffsetIndex(xs: ArrayLike<number>, value: number) { return value < xs[min] ? min - 1 : min; } -function getAtE(set: AtomSetElements, i: number): Atom { - const { offsets, keys } = set; - const o = getOffsetIndex(offsets, i); - if (o >= offsets.length - 1) return 0 as any; - const k = keys[o]; - const e = OrderedSet.getAt(set.sets.get(k), i - offsets[o]); - return Atom.create(k, e); -} - -function indexOfE(set: AtomSetElements, t: Atom) { - const { keys } = set; - const u = Atom.unit(t); - const setIdx = SortedArray.indexOf(keys, u); - if (setIdx < 0) return -1; - const o = OrderedSet.indexOf(set.sets.get(u), Atom.index(t)); - if (o < 0) return -1; - return set.offsets[setIdx] + o; -} - -function computeHash(set: AtomSetElements) { - const { keys, sets } = set; +function computeHash(set: AtomSetImpl) { + 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 + OrderedSet.hashCode(sets.get(k))) | 0; + hash = (31 * hash + AtomGroup.hashCode(groups.get(k))) | 0; } hash = (31 * hash + size(set)) | 0; hash = hash1(hash); @@ -313,177 +384,43 @@ function computeHash(set: AtomSetElements) { return hash; } -function areEqualEE(a: AtomSetElements, b: AtomSetElements) { - 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 { sets: aSets } = a; - const { sets: bSets } = b; - for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - if (!OrderedSet.areEqual(aSets.get(k), bSets.get(k))) return false; - } - return true; -} - -function areIntersectingNE(a: Atom, b: AtomSetElements) { - const u = Atom.unit(a); - return b.sets.has(u) && OrderedSet.has(b.sets.get(u), Atom.index(a)); -} - -function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) { - 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 { sets: aSets } = a; - const { sets: bSets } = b; - for (let i = start; i < end; i++) { - const k = keysA[i]; - const ak = aSets.get(k), bk = bSets.get(k); - if (!!ak && !!bk && OrderedSet.areIntersecting(ak, bk)) return true; - } - return false; -} - -function intersectNE(a: Atom, b: AtomSetElements) { - const u = Atom.unit(a); - return b.sets.has(u) && OrderedSet.has(b.sets.get(u), Atom.index(a)) ? a : Empty; -} - -function intersectEE(a: AtomSetElements, b: AtomSetElements) { - 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 { sets: aSets } = a; - const { sets: bSets } = b; - const generator = Generator(); - for (let i = start; i < end; i++) { - const k = keysA[i]; - const bk = bSets.get(k); - if (!bk) continue; - generator.add(k, OrderedSet.intersect(aSets.get(k), bk)); - } - return generator.getSet(); -} - -function subtractNE(a: Atom, b: AtomSetElements) { - return hasAtom(b, a) ? Empty : a; -} - -function subtractEN(a: AtomSetElements, b: Atom): AtomSetImpl { - if (!hasAtom(a, b)) return a; - - const u = Atom.unit(b), v = Atom.index(b); - const { sets: aSets } = a; - const set = aSets.get(u); - - if (OrderedSet.size(set) === 1) { - // remove the entire unit. - const generator = Generator(); - for (let i = 0, _i = a.keys.length; i < _i; i++) { - const k = a.keys[i]; - if (k !== u) generator.add(k, aSets.get(k)) - } - return generator.getSet(); - } else { - const generator = Generator(); - for (let i = 0, _i = a.keys.length; i < _i; i++) { - const k = a.keys[i]; - if (k === u) generator.add(k, OrderedSet.subtract(set, OrderedSet.ofSingleton(v))) - else generator.add(k, aSets.get(k)) - } - return generator.getSet(); - } -} - -function subtractEE(a: AtomSetElements, b: AtomSetElements) { - 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 = Generator(); - const { sets: aSets } = a; - const { sets: bSets } = b; - for (let i = 0; i < start; i++) { - const k = keysA[i]; - generator.add(k, aSets.get(k)); - } - for (let i = start; i < end; i++) { - const k = keysA[i]; - const ak = aSets.get(k), bk = bSets.get(k); - if (!!bk) { - const subtraction = OrderedSet.subtract(ak, bk); - generator.add(k, subtraction); - } else { - generator.add(k, ak); - } - } - for (let i = end, _i = keysA.length; i < _i; i++) { - const k = keysA[i]; - generator.add(k, aSets.get(k)); - } - return generator.getSet(); -} - -function findUnion(sets: ArrayLike<AtomSetImpl>) { +function findUnion(sets: ArrayLike<AtomSetImpl>, template: AtomSetImpl) { if (!sets.length) return Empty; if (sets.length === 1) return sets[0]; - if (sets.length === 2 && areEqual(sets[0], sets[1])) return sets[0]; + if (sets.length === 2 && sets[0] === sets[1]) return sets[0]; - const eCount = { count: 0 }; - const ns = unionN(sets, eCount); - if (!eCount.count) return ns; - const generator = Generator(); + const keys: number[] = []; + const groups = IntMap.Mutable<AtomGroup>(); for (let i = 0, _i = sets.length; i < _i; i++) { - const s = sets[i]; - if (typeof s !== 'number') unionInto(generator, s as AtomSetElements); + unionInto(keys, groups, sets[i]); } - if (size(ns as AtomSetImpl) > 0) { - if (typeof ns === 'number') unionIntoN(generator, ns as any); - else unionInto(generator, ns as AtomSetElements); - } - return generator.getSet(); -} -function unionN(sets: ArrayLike<AtomSetImpl>, eCount: { count: number }) { - let countN = 0, countE = 0; - for (let i = 0, _i = sets.length; i < _i; i++) { - if (typeof sets[i] === 'number') countN++; - else countE++; - } - eCount.count = countE; - if (!countN) return Empty; - if (countN === sets.length) return ofAtoms(sets as ArrayLike<Atom>); - const packed = Atom.createEmptyArray(countN); - let offset = 0; - for (let i = 0, _i = sets.length; i < _i; i++) { - const s = sets[i]; - if (typeof s === 'number') packed[offset++] = s; - } - return ofAtoms(packed as any); + return normalizeUnion(keys, groups, template); } -function unionInto(builder: AtomSetGenerator, a: AtomSetElements) { - const keys = a.keys; - const { sets: aSets } = a; +function normalizeUnion(keys: number[], groups: IntMap.Mutable<AtomGroup>, template: AtomSetImpl) { + let equalCount = 0; + let tg = template.groups, a: AtomGroup, t: AtomGroup; for (let i = 0, _i = keys.length; i < _i; i++) { const k = keys[i]; - builder.addUnion(k, aSets.get(k)); + if (tg.has(k) && AtomGroup.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 unionIntoN(builder: AtomSetGenerator, a: Atom) { - const u = Atom.unit(a); - builder.addUnion(u, OrderedSet.ofSingleton(Atom.index(a))); +function unionInto(keys: number[], groups: IntMap.Mutable<AtomGroup>, a: AtomSetImpl) { + 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, AtomGroup.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/atom/set.1.ts b/src/mol-model/structure/structure/atom/set.1.ts deleted file mode 100644 index 16431f544f25461ae555b1d3619e67c8adf5e48b..0000000000000000000000000000000000000000 --- a/src/mol-model/structure/structure/atom/set.1.ts +++ /dev/null @@ -1,57 +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 } from 'mol-data/int' -import Atom from '../atom' -import AtomGroup from './group' -import * as Impl from './impl/set.1' -import * as Builders from './impl/builder' - -/** A map-like representation of grouped atom set */ -namespace AtomSet { - export const Empty: AtomSet = Impl.Empty as any; - - export const ofAtoms: (atoms: ArrayLike<Atom>, template: AtomSet) => AtomSet = Impl.ofAtoms as any; - - export const unitCount: (set: AtomSet) => number = Impl.keyCount as any; - export const unitIds: (set: AtomSet) => SortedArray = Impl.getKeys as any; - export const unitHas: (set: AtomSet, id: number) => boolean = Impl.hasKey as any; - export const unitGetId: (set: AtomSet, i: number) => number = Impl.getKey as any; - - export const unitGetById: (set: AtomSet, key: number) => AtomGroup = Impl.getByKey as any; - export const unitGetByIndex: (set: AtomSet, i: number) => AtomGroup = Impl.getByIndex as any; - - export const atomCount: (set: AtomSet) => number = Impl.size as any; - export const atomHas: (set: AtomSet, x: Atom) => boolean = Impl.hasAtom as any; - export const atomIndexOf: (set: AtomSet, x: Atom) => number = Impl.indexOf as any; - export const atomGetAt: (set: AtomSet, i: number) => Atom = Impl.getAt as any; - export const atoms: (set: AtomSet) => Iterator<Atom> = Impl.values as any; - - export const hashCode: (set: AtomSet) => number = Impl.hashCode as any; - export const areEqual: (a: AtomSet, b: AtomSet) => boolean = Impl.areEqual as any; - export const areIntersecting: (a: AtomSet, b: AtomSet) => boolean = Impl.areIntersecting as any; - - export const union: (sets: AtomSet[], template: AtomSet) => AtomSet = Impl.unionMany as any; - export const intersect: (a: AtomSet, b: AtomSet) => AtomSet = Impl.intersect as any; - export const subtract: (a: AtomSet, b: AtomSet) => AtomSet = 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: AtomGroup): void, getSet(): AtomSet } - export const Generator: () => Generator = Impl.Generator as any - export const TemplateGenerator: (template: AtomSet) => Generator = Impl.TemplateGenerator as any - - // TODO: bounding sphere - // TODO: distance, areWithIn? - // TODO: check connected - // TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? -} - -interface AtomSet { '@type': 'atom-set' | Atom['@type'] } - -export default AtomSet \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom/set.ts b/src/mol-model/structure/structure/atom/set.ts index ec63e2e23fe48e13531305af6f33b3f3e309830b..2d675f3f6aba8ac81a418bdfa7b056adff3acaeb 100644 --- a/src/mol-model/structure/structure/atom/set.ts +++ b/src/mol-model/structure/structure/atom/set.ts @@ -4,8 +4,9 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { OrderedSet, SortedArray, Iterator } from 'mol-data/int' +import { SortedArray, Iterator } from 'mol-data/int' import Atom from '../atom' +import AtomGroup from './group' import * as Impl from './impl/set' import * as Builders from './impl/builder' @@ -13,15 +14,15 @@ import * as Builders from './impl/builder' namespace AtomSet { export const Empty: AtomSet = Impl.Empty as any; - export const create: (data: Atom | ArrayLike<Atom>) => AtomSet = Impl.create as any; + export const ofAtoms: (atoms: ArrayLike<Atom>, template: AtomSet) => AtomSet = Impl.ofAtoms as any; export const unitCount: (set: AtomSet) => number = Impl.keyCount as any; export const unitIds: (set: AtomSet) => SortedArray = Impl.getKeys as any; export const unitHas: (set: AtomSet, id: number) => boolean = Impl.hasKey as any; export const unitGetId: (set: AtomSet, i: number) => number = Impl.getKey as any; - export const unitGetById: (set: AtomSet, key: number) => OrderedSet = Impl.getByKey as any; - export const unitGetByIndex: (set: AtomSet, i: number) => OrderedSet = Impl.getByIndex as any; + export const unitGetById: (set: AtomSet, key: number) => AtomGroup = Impl.getByKey as any; + export const unitGetByIndex: (set: AtomSet, i: number) => AtomGroup = Impl.getByIndex as any; export const atomCount: (set: AtomSet) => number = Impl.size as any; export const atomHas: (set: AtomSet, x: Atom) => boolean = Impl.hasAtom as any; @@ -33,8 +34,7 @@ namespace AtomSet { export const areEqual: (a: AtomSet, b: AtomSet) => boolean = Impl.areEqual as any; export const areIntersecting: (a: AtomSet, b: AtomSet) => boolean = Impl.areIntersecting as any; - export const union: (a: AtomSet, b: AtomSet) => AtomSet = Impl.union as any; - export const unionMany: (sets: AtomSet[]) => AtomSet = Impl.unionMany as any; + export const union: (sets: AtomSet[], template: AtomSet) => AtomSet = Impl.unionMany as any; export const intersect: (a: AtomSet, b: AtomSet) => AtomSet = Impl.intersect as any; export const subtract: (a: AtomSet, b: AtomSet) => AtomSet = Impl.subtract as any; @@ -42,8 +42,9 @@ namespace AtomSet { export const LinearBuilder = Builders.LinearBuilder export const UnsortedBuilder = Builders.UnsortedBuilder - export interface Generator { add(unit: number, set: OrderedSet): void, getSet(): AtomSet } + export interface Generator { add(unit: number, set: AtomGroup): void, getSet(): AtomSet } export const Generator: () => Generator = Impl.Generator as any + export const TemplateGenerator: (template: AtomSet) => Generator = Impl.TemplateGenerator as any // TODO: bounding sphere // TODO: distance, areWithIn? diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index 77c37e9ad366fa983db1f3290fd1560d786bc3ce..c4346b036abc4836aae38959a2cc2db54ae3ff54 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -38,16 +38,16 @@ namespace Structure { for (let c = 0; c < chains.count; c++) { const group = AtomGroup.createNew(OrderedSet.ofBounds(chains.segments[c], chains.segments[c + 1])); const unit = Unit.create(model, SymmetryOperator.Default, group); - builder.add(unit, OrderedSet.ofBounds(chains.segments[c], chains.segments[c + 1])); + builder.add(unit, unit.naturalGroup); } return builder.getStructure(); } export interface Builder { - add(unit: Unit, atoms: OrderedSet): void, + add(unit: Unit, atoms: AtomGroup): void, addUnit(unit: Unit): void, - setAtoms(unitId: number, atoms: OrderedSet): void, + setAtoms(unitId: number, atoms: AtomGroup): void, getStructure(): Structure, readonly atomCount: number } @@ -58,9 +58,9 @@ namespace Structure { private atoms = AtomSet.Generator(); atomCount = 0; - add(unit: Unit, atoms: OrderedSet) { const id = this.addUnit(unit); this.setAtoms(id, atoms); } + add(unit: Unit, atoms: AtomGroup) { const id = this.addUnit(unit); this.setAtoms(id, atoms); } addUnit(unit: Unit) { const id = this._unitId++; this.units[id] = unit; return id; } - setAtoms(unitId: number, atoms: OrderedSet) { this.atoms.add(unitId, atoms); this.atomCount += OrderedSet.size(atoms); } + setAtoms(unitId: number, atoms: AtomGroup) { this.atoms.add(unitId, atoms); this.atomCount += AtomGroup.size(atoms); } getStructure(): Structure { return this.atomCount > 0 ? Structure.create(this.units, this.atoms.getSet()) : Empty; } } diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts index 07bb5919a28ed67f6b0427a68ba894c37ff3b5a1..5f7d33928912ffa1cdefcc86202c614415e353d7 100644 --- a/src/perf-tests/sets.ts +++ b/src/perf-tests/sets.ts @@ -2,84 +2,84 @@ import * as B from 'benchmark' import { Tuple, Segmentation, OrderedSet as OrdSet } from 'mol-data/int' import { AtomSet } from 'mol-model/structure' -export namespace Iteration { - const U = 1000, V = 2500; - - const control: number[] = []; - const sets = Object.create(null); - for (let i = 1; i < U; i++) { - const set = []; - for (let j = 1; j < V; j++) { - control[control.length] = j * j + 1; - set[set.length] = j * j + 1; - } - sets[i * i] = OrdSet.ofSortedArray(set); - } - const ms = AtomSet.create(sets); - - export function native() { - let s = 0; - for (let i = 0, _i = control.length; i < _i; i++) s += control[i]; - return s; - } - - export function iterators() { - let s = 0; - const it = AtomSet.atoms(ms); - while (it.hasNext) { - const v = it.move(); - s += Tuple.snd(v); - } - return s; - } - - export function elementAt() { - let s = 0; - for (let i = 0, _i = AtomSet.atomCount(ms); i < _i; i++) s += Tuple.snd(AtomSet.atomGetAt(ms, i)); - return s; - } - - export function manual() { - let s = 0; - const keys = AtomSet.unitIds(ms); - for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) { - const set = AtomSet.unitGetById(ms, OrdSet.getAt(keys, i)); - for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { - s += OrdSet.getAt(set, j); - } - } - return s; - } - - export function manual1() { - let s = 0; - for (let i = 0, _i = AtomSet.unitCount(ms); i < _i; i++) { - const set = AtomSet.unitGetByIndex(ms, i); - for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { - s += OrdSet.getAt(set, j); - } - } - return s; - } - - export function run() { - const suite = new B.Suite(); - - // const values: number[] = []; - // for (let i = 0; i < 1000000; i++) values[i] = (Math.random() * 1000) | 0; - - console.log(Iteration.native(), Iteration.iterators(), Iteration.elementAt(), Iteration.manual(), Iteration.manual1()); - - suite - .add('native', () => Iteration.native()) - .add('iterators', () => Iteration.iterators()) - .add('manual', () => Iteration.manual()) - .add('manual1', () => Iteration.manual1()) - .add('el at', () => Iteration.elementAt()) - .on('cycle', (e: any) => console.log(String(e.target))) - .run(); - } -} +// export namespace Iteration { +// const U = 1000, V = 2500; + +// const control: number[] = []; +// const sets = Object.create(null); +// for (let i = 1; i < U; i++) { +// const set = []; +// for (let j = 1; j < V; j++) { +// control[control.length] = j * j + 1; +// set[set.length] = j * j + 1; +// } +// sets[i * i] = OrdSet.ofSortedArray(set); +// } +// const ms = AtomSet.create(sets); + +// export function native() { +// let s = 0; +// for (let i = 0, _i = control.length; i < _i; i++) s += control[i]; +// return s; +// } + +// export function iterators() { +// let s = 0; +// const it = AtomSet.atoms(ms); +// while (it.hasNext) { +// const v = it.move(); +// s += Tuple.snd(v); +// } +// return s; +// } + +// export function elementAt() { +// let s = 0; +// for (let i = 0, _i = AtomSet.atomCount(ms); i < _i; i++) s += Tuple.snd(AtomSet.atomGetAt(ms, i)); +// return s; +// } + +// export function manual() { +// let s = 0; +// const keys = AtomSet.unitIds(ms); +// for (let i = 0, _i = OrdSet.size(keys); i < _i; i++) { +// const set = AtomSet.unitGetById(ms, OrdSet.getAt(keys, i)); +// for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { +// s += OrdSet.getAt(set, j); +// } +// } +// return s; +// } + +// export function manual1() { +// let s = 0; +// for (let i = 0, _i = AtomSet.unitCount(ms); i < _i; i++) { +// const set = AtomSet.unitGetByIndex(ms, i); +// for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { +// s += OrdSet.getAt(set, j); +// } +// } +// return s; +// } + +// export function run() { +// const suite = new B.Suite(); + +// // const values: number[] = []; +// // for (let i = 0; i < 1000000; i++) values[i] = (Math.random() * 1000) | 0; + +// console.log(Iteration.native(), Iteration.iterators(), Iteration.elementAt(), Iteration.manual(), Iteration.manual1()); + +// suite +// .add('native', () => Iteration.native()) +// .add('iterators', () => Iteration.iterators()) +// .add('manual', () => Iteration.manual()) +// .add('manual1', () => Iteration.manual1()) +// .add('el at', () => Iteration.elementAt()) +// .on('cycle', (e: any) => console.log(String(e.target))) +// .run(); +// } +// } export namespace Union { function createArray(n: number) { diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index d0f9286bcfa152dfd8dc521708c4659546d53e0a..6fdd03c754e5fbd9a87e892dd9fab97e891b3430 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -10,8 +10,8 @@ import * as util from 'util' import * as fs from 'fs' import CIF from 'mol-io/reader/cif' -import { Structure, Model, Queries as Q, Atom, AtomSet, Selection, Symmetry } from 'mol-model/structure' -import { OrderedSet as OrdSet, Segmentation } from 'mol-data/int' +import { Structure, Model, Queries as Q, Atom, AtomGroup, AtomSet, Selection, Symmetry } from 'mol-model/structure' +import { Segmentation } from 'mol-data/int' import to_mmCIF from 'mol-model/structure/export/mmcif' @@ -78,8 +78,8 @@ export namespace PropertyAccess { const set = AtomSet.unitGetByIndex(atoms, i); - for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { - l.atom = OrdSet.getAt(set, j); + for (let j = 0, _j = AtomGroup.size(set); j < _j; j++) { + l.atom = AtomGroup.getAt(set, j); s += p(l); } } @@ -100,20 +100,20 @@ export namespace PropertyAccess { l.unit = unit; const set = AtomSet.unitGetByIndex(atoms, i); - const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set); + const chainsIt = Segmentation.transientSegments(unit.hierarchy.chainSegments, set.atoms); const residues = unit.hierarchy.residueSegments; while (chainsIt.hasNext) { cC++; const chainSegment = chainsIt.move(); - const residuesIt = Segmentation.transientSegments(residues, set, chainSegment); + const residuesIt = Segmentation.transientSegments(residues, set.atoms, chainSegment); while (residuesIt.hasNext) { rC++; const residueSegment = residuesIt.move(); // l.atom = OrdSet.getAt(set, residueSegment.start); // console.log(unit.hierarchy.residues.auth_comp_id.value(unit.residueIndex[l.atom]), l.atom, OrdSet.getAt(set, residueSegment.end)) for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) { - l.atom = OrdSet.getAt(set, j); + l.atom = AtomGroup.getAt(set, j); vA++; s += p(l); } @@ -137,9 +137,9 @@ export namespace PropertyAccess { const unit = units[unitIds[i]]; l.unit = unit; const set = AtomSet.unitGetByIndex(atoms, i); - const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set); + const residuesIt = Segmentation.transientSegments(unit.hierarchy.residueSegments, set.atoms); while (residuesIt.hasNext) { - l.atom = OrdSet.getAt(set, residuesIt.move().start); + l.atom = AtomGroup.getAt(set, residuesIt.move().start); s += p(l); } } @@ -204,8 +204,8 @@ export namespace PropertyAccess { const set = AtomSet.unitGetByIndex(atoms, i); //const { residueIndex, chainIndex } = unit; const p = unit.conformation.atomId.value; - for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { - const aI = OrdSet.getAt(set, j); + for (let j = 0, _j = AtomGroup.size(set); j < _j; j++) { + const aI = AtomGroup.getAt(set, j); s += p(aI); } }