Skip to content
Snippets Groups Projects
Commit a7b35130 authored by David Sehnal's avatar David Sehnal
Browse files

more atomset refactoring

parent f576e797
Branches
Tags
No related merge requests found
......@@ -132,8 +132,8 @@ function unionII(a: I, b: I) {
if (I.areEqual(a, b)) return a;
const sizeA = I.size(a), sizeB = I.size(b);
if (!sizeA) return b;
if (!sizeB) return a;
if (!sizeA) return b;
const minA = I.min(a), minB = I.min(b);
if (areRangesIntersecting(a, b)) return I.ofRange(Math.min(minA, minB), Math.max(I.max(a), I.max(b)));
let lSize, lMin, rSize, rMin;
......
/**
* 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
......@@ -45,7 +45,7 @@ export class Builder {
const l = unit.length;
if (!this.sorted && l > 1) sortArray(unit);
const set = l === 1 ? OrderedSet.ofSingleton(unit[0]) : OrderedSet.ofSortedArray(unit);
const set = OrderedSet.ofSortedArray(unit);
const parentSet = AtomSet.unitGetById(this.parent, k);
if (OrderedSet.areEqual(set, parentSet)) {
generator.add(k, parentSet);
......
......@@ -218,18 +218,17 @@ export class TemplateAtomSetGenerator {
add(unit: number, group: AtomGroup) {
if (AtomGroup.size(group) === 0) return;
this.keys[this.keys.length] = unit;
const templ = this.templateGroups.get(unit);
if (AtomGroup.areEqual(templ, group)) {
this.groups.set(unit, templ);
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) {
if (this.equalGroups === this.template.keys.length && this.equalGroups === this.keys.length) {
return this.template;
}
return create(this.keys, this.groups);
......@@ -401,16 +400,15 @@ function findUnion(sets: ArrayLike<AtomSetImpl>, template: AtomSetImpl) {
function normalizeUnion(keys: number[], groups: IntMap.Mutable<AtomGroup>, template: AtomSetImpl) {
let equalCount = 0;
let tg = template.groups;
let tg = template.groups, a: AtomGroup, t: AtomGroup;
for (let i = 0, _i = keys.length; i < _i; i++) {
const k = keys[i];
const a = groups.get(k), t = tg.get(k);
if (AtomGroup.areEqual(a, t)) {
if (tg.has(k) && AtomGroup.areEqual(a = groups.get(k), t = tg.get(k))) {
groups.set(k, t);
equalCount++;
}
}
return equalCount === template.keys.length ? template : create(keys, groups);
return equalCount === template.keys.length && equalCount === keys.length ? template : create(keys, groups);
}
function unionInto(keys: number[], groups: IntMap.Mutable<AtomGroup>, a: AtomSetImpl) {
......@@ -421,6 +419,7 @@ function unionInto(keys: number[], groups: IntMap.Mutable<AtomGroup>, a: AtomSet
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));
}
}
......
/**
* 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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment