diff --git a/src/mol-base/collections/_spec/iterators.spec.ts b/src/mol-base/collections/_spec/iterators.spec.ts index d5b34f52712108bea8d02c7c714dbccbf63379f4..8df989242574b29c3ba0f766f338e87ae7526eaa 100644 --- a/src/mol-base/collections/_spec/iterators.spec.ts +++ b/src/mol-base/collections/_spec/iterators.spec.ts @@ -26,4 +26,5 @@ describe('basic iterators', () => { check('singleton', Iterator.Value(10), [10]); check('array', Iterator.Array([1, 2, 3]), [1, 2, 3]); check('range', Iterator.Range(0, 3), [0, 1, 2, 3]); + check('map', Iterator.map(Iterator.Range(0, 1), x => x + 1), [1, 2]); }); \ No newline at end of file diff --git a/src/mol-base/collections/equivalence-classes.ts b/src/mol-base/collections/equivalence-classes.ts index 2cf5ab8d7a3ae8b45e0ed23a76085da20a374931..5f32c4dc33ec2ba6b870707dd8fb51dd7423585d 100644 --- a/src/mol-base/collections/equivalence-classes.ts +++ b/src/mol-base/collections/equivalence-classes.ts @@ -19,9 +19,10 @@ class EquivalenceClassesImpl<K, V> { add(key: K, a: V) { const hash = this.getHash(a); - if (this.byHash[hash]) { + if (!!this.byHash[hash]) { const groups = this.byHash[hash]; - for (const group of groups) { + for (let i = 0, _i = groups.length; i < _i; i++) { + const group = groups[i]; if (this.areEqual(a, group.value)) { group.keys[group.keys.length] = key; return group.id; diff --git a/src/mol-base/collections/hash-set.ts b/src/mol-base/collections/hash-set.ts index 2d13b16c6b15d3837ab29a4720e85372f68ab0a9..b3ef13b9e891a37c6a98d7f4efee4bf249c5b452 100644 --- a/src/mol-base/collections/hash-set.ts +++ b/src/mol-base/collections/hash-set.ts @@ -16,10 +16,10 @@ class HashSetImpl<T> implements SetLike<T> { add(a: T) { const hash = this.getHash(a); - if (this.byHash[hash]) { + if (!!this.byHash[hash]) { const xs = this.byHash[hash]; - for (const x of xs) { - if (this.areEqual(a, x)) return false; + for (let i = 0, _i = xs.length; i < _i; i++) { + if (this.areEqual(a, xs[i])) return false; } xs[xs.length] = a; this.size++; @@ -34,8 +34,9 @@ class HashSetImpl<T> implements SetLike<T> { has(v: T) { const hash = this.getHash(v); if (!this.byHash[hash]) return false; - for (const x of this.byHash[hash]) { - if (this.areEqual(v, x)) return true; + const xs = this.byHash[hash]; + for (let i = 0, _i = xs.length; i < _i; i++) { + if (this.areEqual(v, xs[i])) return true; } return false; } diff --git a/src/mol-base/collections/iterator.ts b/src/mol-base/collections/iterator.ts index 6b5906801a097c427c3cae0498dfcb059c502840..50ad41cecc7acee294cdede5d4ea83a6bfb45586 100644 --- a/src/mol-base/collections/iterator.ts +++ b/src/mol-base/collections/iterator.ts @@ -63,11 +63,26 @@ class ValueIterator<T> implements Iterator<T> { constructor(private value: T) { } } +class MapIteratorImpl<T, R> implements Iterator<R> { + hasNext: boolean; + + move() { + const v = this.f(this.base.move()); + this.hasNext = this.base.hasNext; + return v; + } + + constructor(private base: Iterator<T>, private f: (v: T) => R) { + this.hasNext = base.hasNext; + } +} + namespace Iterator { export const Empty: Iterator<any> = new RangeIteratorImpl(0, -1); export function Array<T>(xs: ArrayLike<T>): Iterator<T> { return new ArrayIteratorImpl<T>(xs); } export function Value<T>(value: T): Iterator<T> { return new ValueIterator(value); } export function Range(min: number, max: number): Iterator<number> { return new RangeIteratorImpl(min, max); } + export function map<T, R>(base: Iterator<T>, f: (v: T) => R): Iterator<R> { return new MapIteratorImpl(base, f); } } export default Iterator \ No newline at end of file diff --git a/src/mol-base/computation.ts b/src/mol-base/computation.ts index 76af815bca14741dc89fe7e8afa6770fd634b5af..2a4d6075156c0155c21e186273bb2fa25c31ec00 100644 --- a/src/mol-base/computation.ts +++ b/src/mol-base/computation.ts @@ -181,7 +181,9 @@ class ObservableContext implements Computation.Context { if (this.observers) { const p = { ...this.progress }; - for (const o of this.observers) Scheduler.immediate(o, p); + for (let i = 0, _i = this.observers.length; i < _i; i++) { + Scheduler.immediate(this.observers[i], p); + } } this.lastDelta = time - this.lastUpdated; diff --git a/src/mol-data/query/generators.ts b/src/mol-data/query/generators.ts new file mode 100644 index 0000000000000000000000000000000000000000..28ce9a00172e698d50f8e908a3908248acf2ccb9 --- /dev/null +++ b/src/mol-data/query/generators.ts @@ -0,0 +1,5 @@ +/** + * Copyright (c) 2017 molio contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ \ No newline at end of file diff --git a/src/mol-data/query/selection.ts b/src/mol-data/query/selection.ts index 1ae15e6085672dceb5dabf50f18a7405f2166e0d..762c304ac90d1eab80943ec986b3ba48cab861e3 100644 --- a/src/mol-data/query/selection.ts +++ b/src/mol-data/query/selection.ts @@ -4,21 +4,123 @@ * @author David Sehnal <david.sehnal@gmail.com> */ +import Iterator from '../../mol-base/collections/iterator' +import HashSet from '../../mol-base/collections/hash-set' import Structure from './../structure' +import Atom from './../structure/atom' +import AtomSet from './../structure/atom-set' type Selection = | Structure // each atom is interpreted as a singleton structure | Structure[] namespace Selection { - export const structureCount: (sel: Selection) => number = 0 as any; - export const union: (sel: Selection) => Structure = 0 as any; - export const getAt: (sel: Selection, i: number) => Structure = 0 as any; + export const Empty: Selection = []; + + function isStructure(x: Selection): x is Structure { return !!(x as Structure).units && !!(x as Structure).atoms; } + + export function structureCount(sel: Selection) { + if (isStructure(sel)) return AtomSet.atomCount(sel.atoms); + return sel.length; + } + + export function union(sel: Selection): Structure { + if (isStructure(sel)) return sel; + 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 { units: unionUnits(sel), atoms: AtomSet.unionMany(sets) }; + } + + export function structures(sel: Selection): Iterator<Structure> { + if (isStructure(sel)) { + const units = sel.units; + return Iterator.map<Atom, Structure>(AtomSet.atoms(sel.atoms), atoms => ({ units, atoms })); + } + return Iterator.Array(sel); + } + + export function getAt(sel: Selection, i: number): Structure { + if (isStructure(sel)) { + return { units: sel.units, atoms: AtomSet.atomGetAt(sel.atoms, i) }; + } + return sel[i]; + } + + export interface Builder { + add(s: Structure): void, + getSelection(): Selection + } + + class LinearBuilderImpl implements Builder { + private structures: Structure[] = []; + private allSingletons = true; + + add(s: Structure) { + const atomCount = AtomSet.atomCount(s.atoms); + if (atomCount === 0) return; + this.structures[this.structures.length] = s; + if (atomCount !== 1) this.allSingletons = false; + } + + getSelection() { + const len = this.structures.length; + if (len === 0) return Empty; + if (len === 1) return this.structures[0]; + if (this.allSingletons) return union(this.structures); + return this.structures; + } + + constructor() { } + } + + class HashBuilderImpl implements Builder { + private structures: Structure[] = []; + private allSingletons = true; + private sets = HashSet(AtomSet.hashCode, AtomSet.areEqual); + + add(s: Structure) { + const atomCount = AtomSet.atomCount(s.atoms); + if (atomCount === 0 || !this.sets.add(s.atoms)) return; + this.structures[this.structures.length] = s; + if (atomCount !== 1) this.allSingletons = false; + } + + getSelection() { + const len = this.structures.length; + if (len === 0) return Empty; + if (len === 1) return this.structures[0]; + if (this.allSingletons) return union(this.structures); + return this.structures; + } + + constructor() { } + } + + export function LinearBuilder(): Builder { return new LinearBuilderImpl(); } + export function UniqueBuilder(): Builder { return new HashBuilderImpl(); } - // TODO: 'structure iterator' - // TODO: selection builders (linear / unique) // TODO: spatial lookup - // TODO: If all structures in a selection are "singletons", collapse them into a single structure } -export default Selection \ No newline at end of file +export default Selection + +function unionUnits(xs: Structure[]): Structure['units'] { + let prev = xs[0].units; + let sameModel = true; + for (let i = 1, _i = xs.length; i < _i; i++) { + if (xs[i].units !== prev) sameModel = false; + } + if (sameModel) return prev; + + let ret: any = { ...prev }; + for (let i = 1, _i = xs.length; i < _i; i++) { + const units = xs[i].units; + if (units !== prev) { + const keys = Object.keys(units); + for (let j = 0; j < keys.length; j++) ret[keys[j]] = (units as any)[keys[j]]; + } + prev = xs[i]; + } + return ret; +} diff --git a/src/mol-data/structure.ts b/src/mol-data/structure.ts index 4f7f150832ef496c0469d232962a01f3f67292b5..cab016bb1db415588ed41cf845676f805ba8235e 100644 --- a/src/mol-data/structure.ts +++ b/src/mol-data/structure.ts @@ -20,7 +20,6 @@ interface Structure extends Readonly<{ namespace Structure { export const Empty = Base.Empty; export const ofModel = Base.ofModel; - // TODO: "lift" atom set operators // TODO: "diff" } diff --git a/src/mol-data/structure/atom-set.ts b/src/mol-data/structure/atom-set.ts index a0524ecdf51be150843a34f6742b201387570909..1e654f113c752bb7e67f903d23359f74340a689e 100644 --- a/src/mol-data/structure/atom-set.ts +++ b/src/mol-data/structure/atom-set.ts @@ -49,6 +49,6 @@ namespace AtomSet { // TODO: add "parent" property? how to avoid using too much memory? Transitive parents? Parent unlinking? } -interface AtomSet { '@type': 'atom-set' } +interface AtomSet { '@type': 'atom-set' | Atom['@type'] } export default AtomSet \ No newline at end of file diff --git a/src/mol-data/structure/atom-set/base.ts b/src/mol-data/structure/atom-set/base.ts index 102fde8fedae4eb6062643e6be21e247b63748f3..d8be533e77bc0dd07c754a40907e08f10252929e 100644 --- a/src/mol-data/structure/atom-set/base.ts +++ b/src/mol-data/structure/atom-set/base.ts @@ -181,8 +181,10 @@ function isArrayLike(x: any): x is ArrayLike<Atom> { function ofObject(data: { [id: number]: OrderedSet }) { const keys = []; - for (const _k of Object.keys(data)) { - const k = +_k; + + 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; } if (!keys.length) return Empty; @@ -263,8 +265,9 @@ function ofAtoms(xs: ArrayLike<Atom>) { } const ret: { [key: number]: OrderedSet } = Object.create(null); const keys = []; - for (const _k of Object.keys(sets)) { - const k = +_k; + 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])); } diff --git a/src/mol-data/structure/atom-set/builder.ts b/src/mol-data/structure/atom-set/builder.ts index 06c8de16db153ee462e921a0dfb7f48c4593de0e..e566c30dccbb7ff95ca71682eef90de2b471343f 100644 --- a/src/mol-data/structure/atom-set/builder.ts +++ b/src/mol-data/structure/atom-set/builder.ts @@ -22,7 +22,7 @@ class Builder { } } - beginUnit() { this.currentUnit = []; } + beginUnit() { this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; } addToUnit(a: number) { this.currentUnit[this.currentUnit.length] = a; } commitUnit(u: number) { if (this.currentUnit.length === 0) return; @@ -32,7 +32,9 @@ class Builder { getSet(): AtomSet { const sets: { [key: number]: OrderedSet } = Object.create(null); - for (const k of this.keys) { + + for (let i = 0, _i = this.keys.length; i < _i; i++) { + const k = this.keys[i]; const unit = this.units[k]; const l = unit.length; if (!this.sorted && l > 1) sortArray(unit); diff --git a/src/mol-data/structure/base.ts b/src/mol-data/structure/base.ts index 9732c6a75d4fd5e794cddd2be2669c21fde70958..210c7dcd5830df8e544358185bb6c940d910a5ac 100644 --- a/src/mol-data/structure/base.ts +++ b/src/mol-data/structure/base.ts @@ -18,7 +18,7 @@ export class StructureBuilder { addUnit(unit: Unit) { this.units[unit.id] = unit; } addAtoms(unitId: number, atoms: OrderedSet) { this.atoms[unitId] = atoms; } - getStructure(): Structure { return { units: this.units, atoms: AtomSet.create(this.atoms) } } + getStructure(): Structure { return { units: this.units, atoms: AtomSet.create(this.atoms) }; } } export const Empty: Structure = { units: {}, atoms: AtomSet.Empty }; diff --git a/src/mol-io/utils/msgpack/encode.ts b/src/mol-io/utils/msgpack/encode.ts index 4c636d3026f8716710dfafa8614f802b7a0b60e9..7056ceebe5fd1bd23f7af085dbd1383b61568b00 100644 --- a/src/mol-io/utils/msgpack/encode.ts +++ b/src/mol-io/utils/msgpack/encode.ts @@ -284,7 +284,8 @@ function encodeInternal(value: any, view: DataView, bytes: Uint8Array, offset: n } } else { - for (let key of keys!) { + for (let i = 0, _i = keys!.length; i < _i; i++) { + const key = keys![i]; size += encodeInternal(key, view, bytes, offset + size); size += encodeInternal(value[key], view, bytes, offset + size); }