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

Structure and selection cosntruction updates

parent 59f5f0ca
No related branches found
No related tags found
No related merge requests found
......@@ -16,6 +16,11 @@ describe('sortedArray', () => {
it(name, () => expect(a).toEqual(b));
}
function compareArrays(a: ArrayLike<number>, b: ArrayLike<number>) {
expect(a.length).toBe(b.length);
for (let i = 0; i < a.length; i++) expect(a[i]).toBe(b[i]);
}
const a1234 = SortedArray.ofSortedArray([1, 2, 3, 4]);
const a2468 = SortedArray.ofSortedArray([2, 4, 6, 8]);
......@@ -48,6 +53,12 @@ describe('sortedArray', () => {
testI('findRange', SortedArray.findRange(a2468, 2, 4), Interval.ofRange(0, 1));
it('deduplicate', () => {
compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 1, 1, 1])), [1]);
compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 1, 2, 2, 3, 4])), [1, 2, 3, 4]);
compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 2, 3])), [1, 2, 3]);
});
// console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
// console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
});
\ No newline at end of file
......@@ -273,6 +273,22 @@ export function subtract(a: Nums, b: Nums) {
return ofSortedArray(indices);
}
export function deduplicate(xs: Nums) {
if (xs.length < 2) return xs;
let count = 1;
for (let i = 0, _i = xs.length - 1; i < _i; i++) {
if (xs[i] !== xs[i + 1]) count++;
}
if (count === xs.length) return xs;
const ret = new Int32Array(count);
let o = 0;
for (let i = 0, _i = xs.length - 1; i < _i; i++) {
if (xs[i] !== xs[i + 1]) ret[o++] = xs[i];
}
ret[o] = xs[xs.length - 1];
return ret;
}
const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
// for small sets, just gets the whole range, for large sets does a bunch of binary searches
function getSuitableIntersectionRange(a: Nums, b: Nums) {
......
......@@ -40,6 +40,8 @@ namespace SortedArray {
export const findPredecessorIndex: (array: SortedArray, x: number) => number = Impl.findPredecessorIndex as any;
export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any;
export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
export const deduplicate: (arrat: SortedArray) => SortedArray = Impl.deduplicate as any;
}
interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' }
......
......@@ -7,8 +7,9 @@
import Query from './query'
import Selection from './selection'
import P from './properties'
import { Structure, Element, Unit } from '../structure'
import { Element, Unit } from '../structure'
import { OrderedSet, Segmentation } from 'mol-data/int'
import { LinearGroupingBuilder } from './utils/builders';
export const all: Query.Provider = async (s, ctx) => Selection.Singletons(s, s);
......@@ -113,55 +114,6 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A
};
}
class LinearGroupingBuilder {
private builders: Structure.SubsetBuilder[] = [];
private builderMap = new Map<string, Structure.SubsetBuilder>();
add(key: any, unit: number, element: number) {
let b = this.builderMap.get(key);
if (!b) {
b = this.source.subsetBuilder(true);
this.builders[this.builders.length] = b;
this.builderMap.set(key, b);
}
b.addToUnit(unit, element);
}
private allSingletons() {
for (let i = 0, _i = this.builders.length; i < _i; i++) {
if (this.builders[i].elementCount > 1) return false;
}
return true;
}
private singletonSelection(): Selection {
const builder = this.source.subsetBuilder(true);
const loc = Element.Location();
for (let i = 0, _i = this.builders.length; i < _i; i++) {
this.builders[i].setSingletonLocation(loc);
builder.addToUnit(loc.unit.id, loc.element);
}
return Selection.Singletons(this.source, builder.getStructure());
}
private fullSelection() {
const structures: Structure[] = new Array(this.builders.length);
for (let i = 0, _i = this.builders.length; i < _i; i++) {
structures[i] = this.builders[i].getStructure();
}
return Selection.Sequence(this.source, structures);
}
getSelection(): Selection {
const len = this.builders.length;
if (len === 0) return Selection.Empty(this.source);
if (this.allSingletons()) return this.singletonSelection();
return this.fullSelection();
}
constructor(private source: Structure) { }
}
function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomGroupsQueryParams): Query.Provider {
return async (structure, ctx) => {
const { units } = structure;
......
......@@ -6,7 +6,7 @@
import { HashSet } from 'mol-data/generic'
import { Structure } from '../structure'
import { StructureUtils } from './utils';
import { structureUnion } from './utils/structure';
// A selection is a pair of a Structure and a sequence of unique AtomSets
type Selection = Selection.Singletons | Selection.Sequence
......@@ -31,7 +31,7 @@ namespace Selection {
export function unionStructure(sel: Selection): Structure {
if (isEmpty(sel)) return Structure.Empty;
if (isSingleton(sel)) return sel.structure;
return StructureUtils.union(sel.source, sel.structures);
return structureUnion(sel.source, sel.structures);
}
export interface Builder {
......@@ -42,7 +42,7 @@ namespace Selection {
function getSelection(source: Structure, structures: Structure[], allSingletons: boolean) {
const len = structures.length;
if (len === 0) return Empty(source);
if (allSingletons) return Singletons(source, StructureUtils.union(source, structures));
if (allSingletons) return Singletons(source, structureUnion(source, structures));
return Sequence(source, structures);
}
......
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Structure, Unit } from '../structure'
import { SortedArray } from 'mol-data/int';
namespace StructureUtils {
export function union(source: Structure, structures: Structure[]) {
if (structures.length === 0) return Structure.Empty;
if (structures.length === 1) return structures[0];
const unitMap = new Map<number, SortedArray>();
const fullUnits = new Set<number>();
for (const { units } of structures) {
for (let i = 0, _i = units.length; i < _i; i++) {
const u = units[i];
if (unitMap.has(u.id)) {
// check if there is anything more to union in this particual unit.
if (fullUnits.has(u.id)) continue;
const merged = SortedArray.union(unitMap.get(u.id)!, u.elements);
unitMap.set(u.id, merged);
if (merged.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id);
} else {
unitMap.set(u.id, u.elements);
if (u.elements.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id);
}
}
}
const builder = source.subsetBuilder(true);
unitMap.forEach(buildUnion, builder);
return builder.getStructure();
}
function buildUnion(this: Structure.SubsetBuilder, elements: SortedArray, id: number) {
this.setUnit(id, elements);
}
export function areIntersecting(sA: Structure, sB: Structure): boolean {
if (sA === sB) return true;
let a, b;
if (sA.units.length < sB.units.length) { a = sA; b = sB; }
else { a = sB; b = sA; }
const aU = a.units, bU = b.unitMap;
for (let i = 0, _i = aU.length; i < _i; i++) {
const u = aU[i];
if (!bU.has(u.id)) continue;
const v = bU.get(u.id);
if (SortedArray.areIntersecting(u.elements, v.elements)) return true;
}
return false;
}
export function intersect(sA: Structure, sB: Structure): Structure {
if (sA === sB) return sA;
if (!areIntersecting(sA, sB)) return Structure.Empty;
let a, b;
if (sA.units.length < sB.units.length) { a = sA; b = sB; }
else { a = sB; b = sA; }
const aU = a.units, bU = b.unitMap;
const units: Unit[] = [];
for (let i = 0, _i = aU.length; i < _i; i++) {
const u = aU[i];
if (!bU.has(u.id)) continue;
const v = bU.get(u.id);
if (SortedArray.areIntersecting(u.elements, v.elements)) {
const int = SortedArray.intersect(u.elements, v.elements);
units[units.length] = u.getChild(int);
}
}
return Structure.create(units);
}
export function subtract(a: Structure, b: Structure): Structure {
if (a === b) return Structure.Empty;
if (!areIntersecting(a, b)) return a;
const aU = a.units, bU = b.unitMap;
const units: Unit[] = [];
for (let i = 0, _i = aU.length; i < _i; i++) {
const u = aU[i];
if (!bU.has(u.id)) continue;
const v = bU.get(u.id);
const sub = SortedArray.intersect(u.elements, v.elements);
if (sub.length > 0) {
units[units.length] = u.getChild(sub);
}
}
return Structure.create(units);
}
}
export { StructureUtils }
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Element, Structure } from '../../structure';
import Selection from '../selection';
import { HashSet } from 'mol-data/generic';
import { structureUnion } from './structure';
export class UniqueStructuresBuilder {
private set = HashSet(Structure.hashCode, Structure.areEqual);
private structures: Structure[] = [];
private allSingletons = true;
add(s: Structure) {
if (!s.elementCount) return;
if (s.elementCount !== 1) this.allSingletons = false;
if (this.set.add(s)) {
this.structures[this.structures.length] = s;
}
}
getSelection() {
if (this.allSingletons) return Selection.Singletons(this.source, structureUnion(this.source, this.structures));
return Selection.Sequence(this.source, this.structures);
}
constructor(private source: Structure) {
}
}
export class LinearGroupingBuilder {
private builders: Structure.SubsetBuilder[] = [];
private builderMap = new Map<string, Structure.SubsetBuilder>();
add(key: any, unit: number, element: number) {
let b = this.builderMap.get(key);
if (!b) {
b = this.source.subsetBuilder(true);
this.builders[this.builders.length] = b;
this.builderMap.set(key, b);
}
b.addToUnit(unit, element);
}
private allSingletons() {
for (let i = 0, _i = this.builders.length; i < _i; i++) {
if (this.builders[i].elementCount > 1) return false;
}
return true;
}
private singletonSelection(): Selection {
const builder = this.source.subsetBuilder(true);
const loc = Element.Location();
for (let i = 0, _i = this.builders.length; i < _i; i++) {
this.builders[i].setSingletonLocation(loc);
builder.addToUnit(loc.unit.id, loc.element);
}
return Selection.Singletons(this.source, builder.getStructure());
}
private fullSelection() {
const structures: Structure[] = new Array(this.builders.length);
for (let i = 0, _i = this.builders.length; i < _i; i++) {
structures[i] = this.builders[i].getStructure();
}
return Selection.Sequence(this.source, structures);
}
getSelection(): Selection {
const len = this.builders.length;
if (len === 0) return Selection.Empty(this.source);
if (this.allSingletons()) return this.singletonSelection();
return this.fullSelection();
}
constructor(private source: Structure) { }
}
\ No newline at end of file
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { Structure, Unit } from '../../structure'
import { SortedArray } from 'mol-data/int';
export function structureUnion(source: Structure, structures: Structure[]) {
if (structures.length === 0) return Structure.Empty;
if (structures.length === 1) return structures[0];
const unitMap = new Map<number, SortedArray>();
const fullUnits = new Set<number>();
for (const { units } of structures) {
for (let i = 0, _i = units.length; i < _i; i++) {
const u = units[i];
if (unitMap.has(u.id)) {
// check if there is anything more to union in this particual unit.
if (fullUnits.has(u.id)) continue;
const merged = SortedArray.union(unitMap.get(u.id)!, u.elements);
unitMap.set(u.id, merged);
if (merged.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id);
} else {
unitMap.set(u.id, u.elements);
if (u.elements.length === source.unitMap.get(u.id).elements.length) fullUnits.add(u.id);
}
}
}
const builder = source.subsetBuilder(true);
unitMap.forEach(buildUnion, builder);
return builder.getStructure();
}
function buildUnion(this: Structure.SubsetBuilder, elements: SortedArray, id: number) {
this.setUnit(id, elements);
}
export function structureAreIntersecting(sA: Structure, sB: Structure): boolean {
if (sA === sB) return true;
let a, b;
if (sA.units.length < sB.units.length) { a = sA; b = sB; }
else { a = sB; b = sA; }
const aU = a.units, bU = b.unitMap;
for (let i = 0, _i = aU.length; i < _i; i++) {
const u = aU[i];
if (!bU.has(u.id)) continue;
const v = bU.get(u.id);
if (SortedArray.areIntersecting(u.elements, v.elements)) return true;
}
return false;
}
export function structureIntersect(sA: Structure, sB: Structure): Structure {
if (sA === sB) return sA;
if (!structureAreIntersecting(sA, sB)) return Structure.Empty;
let a, b;
if (sA.units.length < sB.units.length) { a = sA; b = sB; }
else { a = sB; b = sA; }
const aU = a.units, bU = b.unitMap;
const units: Unit[] = [];
for (let i = 0, _i = aU.length; i < _i; i++) {
const u = aU[i];
if (!bU.has(u.id)) continue;
const v = bU.get(u.id);
if (SortedArray.areIntersecting(u.elements, v.elements)) {
const int = SortedArray.intersect(u.elements, v.elements);
units[units.length] = u.getChild(int);
}
}
return Structure.create(units);
}
export function structureSubtract(a: Structure, b: Structure): Structure {
if (a === b) return Structure.Empty;
if (!structureAreIntersecting(a, b)) return a;
const aU = a.units, bU = b.unitMap;
const units: Unit[] = [];
for (let i = 0, _i = aU.length; i < _i; i++) {
const u = aU[i];
if (!bU.has(u.id)) continue;
const v = bU.get(u.id);
const sub = SortedArray.intersect(u.elements, v.elements);
if (sub.length > 0) {
units[units.length] = u.getChild(sub);
}
}
return Structure.create(units);
}
\ No newline at end of file
......@@ -181,7 +181,7 @@ namespace Structure {
this.elementCount += elements.length;
}
getStructure(): Structure {
private _getStructure(deduplicateElements: boolean): Structure {
if (this.isEmpty) return Structure.Empty;
const newUnits: Unit[] = [];
......@@ -193,7 +193,15 @@ namespace Structure {
const id = this.ids[i];
const parent = this.parent.unitMap.get(id);
const unit = this.unitMap.get(id);
let unit: ArrayLike<number> = this.unitMap.get(id);
let sorted = false;
if (deduplicateElements) {
if (!this.isSorted) sortArray(unit);
unit = SortedArray.deduplicate(SortedArray.ofSortedArray(this.currentUnit));
sorted = true;
}
const l = unit.length;
// if the length is the same, just copy the old unit.
......@@ -203,7 +211,7 @@ namespace Structure {
continue;
}
if (!this.isSorted && l > 1) sortArray(unit);
if (!this.isSorted && !sorted && l > 1) sortArray(unit);
let child = parent.getChild(SortedArray.ofSortedArray(unit));
const pivot = symmGroups.add(child.id, child);
......@@ -214,6 +222,10 @@ namespace Structure {
return create(newUnits);
}
getStructure(deduplicateElements = false) {
return this._getStructure(deduplicateElements);
}
setSingletonLocation(location: Element.Location) {
const id = this.ids[0];
location.unit = this.parent.unitMap.get(id);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment