From 60d02b1f6ada49e5b83f81c90583e05cad747e4a Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Fri, 10 Nov 2017 15:58:45 +0100 Subject: [PATCH] Refactoring AtomSet (part 2) --- src/mol-data/int/map.ts | 4 +- src/mol-data/util/equivalence-classes.ts | 8 +- src/mol-data/util/hash-set.ts | 12 +- .../structure/_spec/atom-set.spec.ts | 14 +- src/mol-model/structure/query/generators.ts | 6 +- src/mol-model/structure/structure/atom/set.ts | 13 +- .../structure/structure/atom/set/builder.ts | 30 +-- .../structure/structure/atom/set/impl.ts | 206 +++++++++--------- .../structure/structure/structure.ts | 12 +- src/perf-tests/structure.ts | 5 +- 10 files changed, 154 insertions(+), 156 deletions(-) diff --git a/src/mol-data/int/map.ts b/src/mol-data/int/map.ts index 9c2c2aca9..4d75644ed 100644 --- a/src/mol-data/int/map.ts +++ b/src/mol-data/int/map.ts @@ -6,12 +6,14 @@ import { iterableToArray } from '../util' +// TODO: rename to "linear map" and just do key value mapping from index? + /** Immutable by convention IntMap */ interface IntMap<T> { has(key: number): boolean, keys(): IterableIterator<number>, values(): IterableIterator<T>, - get(key: number): T | undefined, + get(key: number): T, readonly size: number } diff --git a/src/mol-data/util/equivalence-classes.ts b/src/mol-data/util/equivalence-classes.ts index cc5f4af62..e40456499 100644 --- a/src/mol-data/util/equivalence-classes.ts +++ b/src/mol-data/util/equivalence-classes.ts @@ -6,7 +6,7 @@ class EquivalenceClassesImpl<K, V> { private id = 0; - private byHash: { [hash: number]: { id: number, keys: K[], value: V }[] } = Object.create(null); + private byHash = new Map<number, { id: number, keys: K[], value: V }[]>(); readonly groups: K[][] = []; @@ -19,8 +19,8 @@ class EquivalenceClassesImpl<K, V> { add(key: K, a: V) { const hash = this.getHash(a); - if (!!this.byHash[hash]) { - const groups = this.byHash[hash]; + if (this.byHash.has(hash)) { + const groups = this.byHash.get(hash)!; for (let i = 0, _i = groups.length; i < _i; i++) { const group = groups[i]; if (this.areEqual(a, group.value)) { @@ -33,7 +33,7 @@ class EquivalenceClassesImpl<K, V> { return group.id; } else { const group = this.createGroup(key, a); - this.byHash[hash] = [group]; + this.byHash.set(hash, [group]); return group.id; } } diff --git a/src/mol-data/util/hash-set.ts b/src/mol-data/util/hash-set.ts index 0e41ad62f..cbcf7560a 100644 --- a/src/mol-data/util/hash-set.ts +++ b/src/mol-data/util/hash-set.ts @@ -12,12 +12,12 @@ interface SetLike<T> { class HashSetImpl<T> implements SetLike<T> { size: number = 0; - private byHash: { [hash: number]: T[] } = Object.create(null); + private byHash = new Map<number, T[]>(); add(a: T) { const hash = this.getHash(a); - if (!!this.byHash[hash]) { - const xs = this.byHash[hash]; + if (this.byHash.has(hash)) { + const xs = this.byHash.get(hash)!; for (let i = 0, _i = xs.length; i < _i; i++) { if (this.areEqual(a, xs[i])) return false; } @@ -25,7 +25,7 @@ class HashSetImpl<T> implements SetLike<T> { this.size++; return true; } else { - this.byHash[hash] = [a]; + this.byHash.set(hash, [a]); this.size++; return true; } @@ -33,8 +33,8 @@ class HashSetImpl<T> implements SetLike<T> { has(v: T) { const hash = this.getHash(v); - if (!this.byHash[hash]) return false; - const xs = this.byHash[hash]; + if (!this.byHash.has(hash)) return false; + const xs = this.byHash.get(hash)!; for (let i = 0, _i = xs.length; i < _i; i++) { if (this.areEqual(v, xs[i])) return true; } diff --git a/src/mol-model/structure/_spec/atom-set.spec.ts b/src/mol-model/structure/_spec/atom-set.spec.ts index d77dd5e86..c62081f34 100644 --- a/src/mol-model/structure/_spec/atom-set.spec.ts +++ b/src/mol-model/structure/_spec/atom-set.spec.ts @@ -35,10 +35,10 @@ describe('atom set', () => { }); it('multi', () => { - const set = AtomSet.create({ - 1: OrderedSet.ofSortedArray([4, 6, 7]), - 3: OrderedSet.ofRange(0, 1), - }); + const gen = AtomSet.Generator(); + gen.add(1, OrderedSet.ofSortedArray([4, 6, 7])); + gen.add(3, 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)]); @@ -52,16 +52,16 @@ describe('atom set', () => { it('element at / index of', () => { const control: Atom[] = []; - const sets = Object.create(null); + 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; } - sets[i * i] = OrderedSet.ofSortedArray(set); + gen.add(i * i, OrderedSet.ofSortedArray(set)); } - const ms = AtomSet.create(sets); + const ms = gen.getSet(); for (let i = 0; i < control.length; i++) { expect(Atom.areEqual(AtomSet.atomGetAt(ms, i), control[i])).toBe(true); } diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts index 12d43abc4..305488104 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -105,14 +105,14 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A class LinearGroupingBuilder { private builders: AtomSet.Builder[] = []; - private builderMap: { [key: string]: AtomSet.Builder } = Object.create(null); + private builderMap = new Map<string, AtomSet.Builder>(); add(key: any, unit: number, atom: number) { - let b = this.builderMap[key]; + let b = this.builderMap.get(key); if (!b) { b = AtomSet.LinearBuilder(this.structure.atoms); this.builders[this.builders.length] = b; - this.builderMap[key] = b; + this.builderMap.set(key, b); } b.add(unit, atom); } diff --git a/src/mol-model/structure/structure/atom/set.ts b/src/mol-model/structure/structure/atom/set.ts index 3ce250e24..9df795ece 100644 --- a/src/mol-model/structure/structure/atom/set.ts +++ b/src/mol-model/structure/structure/atom/set.ts @@ -7,13 +7,13 @@ import { OrderedSet, SortedArray, Iterator } from 'mol-data/int' import Atom from '../atom' import * as Impl from './set/impl' -import createBuilder, { Builder as AtomSetBuilder } from './set/builder' +import * as Builders from './set/builder' /** A map-like representation of grouped atom set */ namespace AtomSet { export const Empty: AtomSet = Impl.Empty as any; - export const create: (data: Atom | ArrayLike<Atom> | { [unitId: number]: OrderedSet }) => AtomSet = Impl.create as any; + export const create: (data: Atom | ArrayLike<Atom>) => AtomSet = Impl.create as any; export const unitCount: (set: AtomSet) => number = Impl.keyCount as any; export const unitIds: (set: AtomSet) => SortedArray = Impl.getKeys as any; @@ -38,9 +38,12 @@ namespace AtomSet { 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 = AtomSetBuilder - export function LinearBuilder(parent: AtomSet): Builder { return createBuilder(parent, true); } - export function UnsortedBuilder(parent: AtomSet): Builder { return createBuilder(parent, false); } + export type Builder = Builders.Builder + export const LinearBuilder = Builders.LinearBuilder + export const UnsortedBuilder = Builders.UnsortedBuilder + + export interface Generator { add(unit: number, set: OrderedSet): void, getSet(): AtomSet } + export const Generator: () => Generator = Impl.Generator as any // TODO: bounding sphere // TODO: distance, areWithIn? diff --git a/src/mol-model/structure/structure/atom/set/builder.ts b/src/mol-model/structure/structure/atom/set/builder.ts index fdda1a1c5..798283deb 100644 --- a/src/mol-model/structure/structure/atom/set/builder.ts +++ b/src/mol-model/structure/structure/atom/set/builder.ts @@ -6,21 +6,21 @@ import AtomSet from '../set' import Atom from '../../atom' -import { OrderedSet } from 'mol-data/int' +import { OrderedSet, IntMap } from 'mol-data/int' import { sortArray } from 'mol-data/util/sort' export class Builder { private keys: number[] = []; - private units: number[][] = Object.create(null); + private units = IntMap.Mutable<number[]>(); private currentUnit: number[] = []; atomCount = 0; add(u: number, a: number) { - const unit = this.units[u]; + const unit = this.units.get(u); if (!!unit) { unit[unit.length] = a; } else { - this.units[u] = [a]; + this.units.set(u, [a]); this.keys[this.keys.length] = u; } this.atomCount++; @@ -31,40 +31,44 @@ export class Builder { commitUnit(u: number) { if (this.currentUnit.length === 0) return; this.keys[this.keys.length] = u; - this.units[u] = this.currentUnit; + this.units.set(u, this.currentUnit); } getSet(): AtomSet { - const sets: { [key: number]: OrderedSet } = Object.create(null); + const generator = AtomSet.Generator(); let allEqual = this.keys.length === AtomSet.unitCount(this.parent); for (let i = 0, _i = this.keys.length; i < _i; i++) { const k = this.keys[i]; - const unit = this.units[k]; + const unit = this.units.get(k); const l = unit.length; if (!this.sorted && l > 1) sortArray(unit); const set = l === 1 ? OrderedSet.ofSingleton(unit[0]) : OrderedSet.ofSortedArray(unit); const parentSet = AtomSet.unitGetById(this.parent, k); if (OrderedSet.areEqual(set, parentSet)) { - sets[k] = parentSet; + generator.add(k, parentSet); } else { - sets[k] = set; + generator.add(k, set); allEqual = false; } } - return allEqual ? this.parent : AtomSet.create(sets); + return allEqual ? this.parent : generator.getSet(); } singleton(): Atom { const u = this.keys[0]; - return Atom.create(u, this.units[u][0]); + return Atom.create(u, this.units.get(u)[0]); } constructor(private parent: AtomSet, private sorted: boolean) { } } -export default function createBuilder(parent: AtomSet, sorted: boolean) { - return new Builder(parent, sorted); +export function LinearBuilder(parent: AtomSet) { + return new Builder(parent, true); +} + +export function UnsortedBuilder(parent: AtomSet) { + return new Builder(parent, false); } \ No newline at end of file diff --git a/src/mol-model/structure/structure/atom/set/impl.ts b/src/mol-model/structure/structure/atom/set/impl.ts index 0ada96dc1..99bd298d1 100644 --- a/src/mol-model/structure/structure/atom/set/impl.ts +++ b/src/mol-model/structure/structure/atom/set/impl.ts @@ -11,15 +11,14 @@ import Atom from '../../atom' /** Long and painful implementation starts here */ -export interface AtomSetElements { sets: IntMap<OrderedSet>, offsets: number[], hashCode: number, keys: SortedArray } +export interface AtomSetElements { sets: IntMap<OrderedSet>, offsets: Int32Array, hashCode: number, keys: SortedArray } export type AtomSetImpl = Atom | AtomSetElements -export const Empty: AtomSetImpl = { sets: IntMap.Empty, offsets: [0], hashCode: 0, keys: SortedArray.Empty }; +export const Empty: AtomSetImpl = { sets: IntMap.Empty, offsets: new Int32Array(1), hashCode: 0, keys: SortedArray.Empty }; -export function create(data: Atom | ArrayLike<Atom> | { [id: number]: OrderedSet }): AtomSetImpl { +export function create(data: Atom | ArrayLike<Atom>): AtomSetImpl { if (typeof data === 'number' || Atom.is(data)) return data; - if (isArrayLike(data)) return ofAtoms(data); - return ofObject(data as { [id: number]: OrderedSet }); + return ofAtoms(data); } export function isSingleton(set: AtomSetImpl) { @@ -154,7 +153,7 @@ 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.sets.get(this.unit); this.currentIndex = 0; this.currentSize = OrderedSet.size(this.currentSet); return true; @@ -172,60 +171,57 @@ export function values(set: AtomSetImpl): Iterator<Atom> { return new ElementsIterator(set as AtomSetElements); } -function isArrayLike(x: any): x is ArrayLike<Atom> { - return x && (typeof x.length === 'number' && (Array.isArray(x) || !!x.buffer)); -} - -function ofObject(data: { [id: number]: OrderedSet }) { - const keys = []; +export class AtomSetGenerator { + private keys: number[] = []; + private sets = IntMap.Mutable<OrderedSet>(); - const _keys = Object.keys(data); - for (let i = 0, _i = _keys.length; i < _i; i++) { - const k = +_keys[i]; - if (OrderedSet.size(data[k]) > 0) keys[keys.length] = k; + add(unit: number, set: OrderedSet) { + if (OrderedSet.size(set) === 0) return; + this.keys[this.keys.length] = unit; + this.sets.set(unit, set); } - if (!keys.length) return Empty; - if (keys.length === 1) { - const set = data[keys[0]]; - if (OrderedSet.size(set) === 1) return Atom.create(keys[0], OrderedSet.getAt(set, 0)); + + 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); + } } - return ofObject1(keys, data); -} -function ofObject1(keys: number[], data: { [id: number]: OrderedSet }) { - if (keys.length === 1) { - const k = keys[0]; - const set = data[k]; - if (OrderedSet.size(set) === 1) return Atom.create(k, OrderedSet.getAt(set, 0)); + getSet(): AtomSetImpl { + return ofKeysAndSets(this.keys, this.sets); } - sortArray(keys); - return _createObjectOrdered(SortedArray.ofSortedArray(keys), data); } -function ofObjectOrdered(keys: SortedArray, data: { [id: number]: OrderedSet }) { - if (keys.length === 1) { - const k = keys[0]; - const set = data[k]; - if (OrderedSet.size(set) === 1) return Atom.create(k, OrderedSet.getAt(set, 0)); - } - return _createObjectOrdered(keys, data); +export function Generator() { + return new AtomSetGenerator(); } -function _createObjectOrdered(keys: SortedArray, data: { [id: number]: OrderedSet }): AtomSetElements { - const sets = IntMap.Mutable<OrderedSet>(); - const offsets = [0]; +function ofKeysAndSetsElemements(keys: number[], sets: IntMap<OrderedSet>): AtomSetElements { + sortArray(keys); let runningSize = 0; + const offsets = new Int32Array(keys.length + 1); for (let i = 0, _i = keys.length; i < _i; i++) { - const k = keys[i]; - const set = data[k]; - sets.set(k, set); - runningSize += OrderedSet.size(set); - offsets[offsets.length] = runningSize; + runningSize += OrderedSet.size(sets.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 { sets, keys, offsets, hashCode: - 1 }; + return ofKeysAndSetsElemements(keys, sets); } -function getUniqueElements(xs: number[]) { +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++; @@ -239,7 +235,7 @@ function getUniqueElements(xs: number[]) { return ret; } -function normalizeArray(xs: number[]) { +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); @@ -249,23 +245,28 @@ function normalizeArray(xs: number[]) { function ofAtoms(xs: ArrayLike<Atom>) { if (xs.length === 0) return Empty; - const sets: { [key: number]: number[] } = Object.create(null); + + 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); - const set = sets[u]; - if (set) set[set.length] = v; - else sets[u] = [v]; + if (elements.has(u)) { + const set = elements.get(u); + set[set.length] = v; + } else { + keys[keys.length] = u; + elements.set(u, [v]); + } } - const ret: { [key: number]: OrderedSet } = Object.create(null); - const keys = []; - const _keys = Object.keys(sets); - for (let i = 0, _i = _keys.length; i < _i; i++) { - const k = +_keys[i]; - keys[keys.length] = k; - ret[k] = OrderedSet.ofSortedArray(normalizeArray(sets[k])); + + const sets = IntMap.Mutable<OrderedSet>(); + for (let i = 0, _i = keys.length; i < _i; i++) { + const k = keys[i]; + sets.set(k, OrderedSet.ofSortedArray(normalizeArray(elements.get(k)))); } - return ofObject1(keys, ret); + + return ofKeysAndSets(keys, sets); } function getOffsetIndex(xs: ArrayLike<number>, value: number) { @@ -288,7 +289,7 @@ function getAtE(set: AtomSetElements, i: number): Atom { 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]); + const e = OrderedSet.getAt(set.sets.get(k), i - offsets[o]); return Atom.create(k, e); } @@ -297,7 +298,7 @@ function indexOfE(set: AtomSetElements, t: Atom) { 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)); + const o = OrderedSet.indexOf(set.sets.get(u), Atom.index(t)); if (o < 0) return -1; return set.offsets[setIdx] + o; } @@ -308,7 +309,7 @@ function computeHash(set: AtomSetElements) { 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 + OrderedSet.hashCode(sets.get(k))) | 0; } hash = (31 * hash + size(set)) | 0; hash = hash1(hash); @@ -326,14 +327,14 @@ function areEqualEE(a: AtomSetElements, b: AtomSetElements) { 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; + 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)); + return b.sets.has(u) && OrderedSet.has(b.sets.get(u), Atom.index(a)); } function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) { @@ -354,7 +355,7 @@ function areIntersectingEE(a: AtomSetElements, b: AtomSetElements) { 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; + return b.sets.has(u) && OrderedSet.has(b.sets.get(u), Atom.index(a)) ? a : Empty; } function intersectEE(a: AtomSetElements, b: AtomSetElements) { @@ -365,20 +366,16 @@ function intersectEE(a: AtomSetElements, b: AtomSetElements) { const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); const start = Interval.start(r), end = Interval.end(r); - const keys = [], ret = Object.create(null); 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; - const intersection = OrderedSet.intersect(aSets.get(k)!, bk); - if (OrderedSet.size(intersection) > 0) { - keys[keys.length] = k; - ret[k] = intersection; - } + generator.add(k, OrderedSet.intersect(aSets.get(k), bk)); } - return ofObjectOrdered(SortedArray.ofSortedArray(keys), ret); + return generator.getSet(); } function subtractNE(a: Atom, b: AtomSetElements) { @@ -390,20 +387,24 @@ function subtractEN(a: AtomSetElements, b: Atom): AtomSetImpl { const u = Atom.unit(b), v = Atom.index(b); const { sets: aSets } = a; - const set = aSets.get(u)!; + const set = aSets.get(u); + if (OrderedSet.size(set) === 1) { // remove the entire unit. - throw 'nyi' - //return ofObjectOrdered(SortedArray.subtract(a.keys, SortedArray.ofSingleton(u)), a); + 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 ret: { [key: number]: OrderedSet } = Object.create(null); + const generator = Generator(); for (let i = 0, _i = a.keys.length; i < _i; i++) { const k = a.keys[i]; - if (k === u) { - ret[k] = OrderedSet.subtract(set, OrderedSet.ofSingleton(v)); - } else ret[k] = aSets.get(k)!; + if (k === u) generator.add(k, OrderedSet.subtract(set, OrderedSet.ofSingleton(v))) + else generator.add(k, aSets.get(k)) } - return ofObjectOrdered(a.keys, ret); + return generator.getSet(); } } @@ -415,34 +416,28 @@ function subtractEE(a: AtomSetElements, b: AtomSetElements) { const r = SortedArray.findRange(keysA, SortedArray.min(keysB), SortedArray.max(keysB)); const start = Interval.start(r), end = Interval.end(r); - const keys = [], ret = Object.create(null); + const generator = Generator(); const { sets: aSets } = a; const { sets: bSets } = b; for (let i = 0; i < start; i++) { const k = keysA[i]; - keys[keys.length] = k; - ret[k] = aSets.get(k); + 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); + const ak = aSets.get(k), bk = bSets.get(k); if (!!bk) { const subtraction = OrderedSet.subtract(ak, bk); - if (OrderedSet.size(subtraction) > 0) { - keys[keys.length] = k; - ret[k] = subtraction; - } + generator.add(k, subtraction); } else { - keys[keys.length] = k; - ret[k] = ak; + generator.add(k, ak); } } for (let i = end, _i = keysA.length; i < _i; i++) { const k = keysA[i]; - keys[keys.length] = k; - ret[k] = aSets.get(k); + generator.add(k, aSets.get(k)); } - return ofObjectOrdered(SortedArray.ofSortedArray(keys), ret); + return generator.getSet(); } function findUnion(sets: ArrayLike<AtomSetImpl>) { @@ -453,16 +448,16 @@ function findUnion(sets: ArrayLike<AtomSetImpl>) { const eCount = { count: 0 }; const ns = unionN(sets, eCount); if (!eCount.count) return ns; - const ret = Object.create(null); + const generator = Generator(); for (let i = 0, _i = sets.length; i < _i; i++) { const s = sets[i]; - if (typeof s !== 'number') unionInto(ret, s as AtomSetElements); + if (typeof s !== 'number') unionInto(generator, s as AtomSetElements); } if (size(ns as AtomSetImpl) > 0) { - if (typeof ns === 'number') unionIntoN(ret, ns as any); - else unionInto(ret, ns as AtomSetElements); + if (typeof ns === 'number') unionIntoN(generator, ns as any); + else unionInto(generator, ns as AtomSetElements); } - return ofObject(ret); + return generator.getSet(); } function unionN(sets: ArrayLike<AtomSetImpl>, eCount: { count: number }) { @@ -483,23 +478,16 @@ function unionN(sets: ArrayLike<AtomSetImpl>, eCount: { count: number }) { return ofAtoms(packed as any); } -function unionInto(data: { [key: number]: OrderedSet }, a: AtomSetElements) { +function unionInto(builder: AtomSetGenerator, a: AtomSetElements) { const keys = a.keys; const { sets: aSets } = a; for (let i = 0, _i = keys.length; i < _i; i++) { const k = keys[i]; - const set = data[k]; - if (set) data[k] = OrderedSet.union(set, aSets.get(k)!); - else data[k] = aSets.get(k)!; + builder.addUnion(k, aSets.get(k)); } } -function unionIntoN(data: { [key: number]: OrderedSet }, a: Atom) { +function unionIntoN(builder: AtomSetGenerator, a: Atom) { const u = Atom.unit(a); - const set = data[u]; - if (set) { - data[u] = OrderedSet.union(set, OrderedSet.ofSingleton(Atom.index(a))); - } else { - data[u] = OrderedSet.ofSingleton(Atom.index(a)); - } + builder.addUnion(u, OrderedSet.ofSingleton(Atom.index(a))); } \ 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 8d5af1dcb..0434bcf8a 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -37,7 +37,7 @@ namespace Structure { for (let c = 0; c < chains.count; c++) { const unit = Unit.create(model, SymmetryOperator.Default); builder.addUnit(unit); - builder.addAtoms(unit.id, OrderedSet.ofBounds(chains.segments[c], chains.segments[c + 1])); + builder.setAtoms(unit.id, OrderedSet.ofBounds(chains.segments[c], chains.segments[c + 1])); } return builder.getStructure(); @@ -46,20 +46,20 @@ namespace Structure { export interface Builder { add(unit: Unit, atoms: OrderedSet): void, addUnit(unit: Unit): void, - addAtoms(unitId: number, atoms: OrderedSet): void, + setAtoms(unitId: number, atoms: OrderedSet): void, getStructure(): Structure, readonly atomCount: number } class BuilderImpl implements Builder { private units = Object.create(null); - private atoms = Object.create(null); + private atoms = AtomSet.Generator(); atomCount = 0; - add(unit: Unit, atoms: OrderedSet) { this.addUnit(unit); this.addAtoms(unit.id, atoms); } + add(unit: Unit, atoms: OrderedSet) { this.addUnit(unit); this.setAtoms(unit.id, atoms); } addUnit(unit: Unit) { this.units[unit.id] = unit; } - addAtoms(unitId: number, atoms: OrderedSet) { this.atoms[unitId] = atoms; this.atomCount += OrderedSet.size(atoms); } - getStructure(): Structure { return this.atomCount > 0 ? Structure.create(this.units, AtomSet.create(this.atoms)) : Empty; } + setAtoms(unitId: number, atoms: OrderedSet) { this.atoms.add(unitId, atoms); this.atomCount += OrderedSet.size(atoms); } + getStructure(): Structure { return this.atomCount > 0 ? Structure.create(this.units, this.atoms.getSet()) : Empty; } } export function Builder(): Builder { return new BuilderImpl(); } diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 0de52636a..d0f9286bc 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -77,6 +77,7 @@ export namespace PropertyAccess { l.unit = units[unitIds[i]]; const set = AtomSet.unitGetByIndex(atoms, i); + for (let j = 0, _j = OrdSet.size(set); j < _j; j++) { l.atom = OrdSet.getAt(set, j); s += p(l); @@ -274,8 +275,8 @@ export namespace PropertyAccess { // return; - console.log(baseline(models[0])); - console.log(sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); + console.log('bs', baseline(models[0])); + console.log('sp', sumProperty(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); console.log(sumPropertySegmented(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); //console.log(sumPropertySegmentedMutable(structures[0], l => l.unit.model.conformation.atomId.value(l.atom))); -- GitLab