Browse Source

selection and various tweaks

David Sehnal 7 years ago
parent
commit
f082d12395

+ 1 - 0
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]);
 });

+ 3 - 2
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;

+ 6 - 5
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;
     }

+ 15 - 0
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

+ 3 - 1
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;

+ 5 - 0
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>
+ */

+ 109 - 7
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
+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;
+}

+ 0 - 1
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"
 }

+ 1 - 1
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

+ 7 - 4
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]));
     }

+ 4 - 2
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);

+ 1 - 1
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 };

+ 2 - 1
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);
             }