diff --git a/src/mol-data/structure/query/generators.ts b/src/mol-data/structure/query/generators.ts index 0479437b9281dbc2bccdc17bfe12f1588a888ce3..a4ca33bd5ccf1c1e5707ad138fcbcf36d66df35a 100644 --- a/src/mol-data/structure/query/generators.ts +++ b/src/mol-data/structure/query/generators.ts @@ -5,8 +5,9 @@ */ import Query from './query' +import Selection from './selection' import * as P from './properties' -import { AtomSet, Atom } from '../structure' +import { Structure, AtomSet, Atom } from '../structure' import { OrderedSet, Segmentation } from 'mol-base/collections/integer' export interface AtomGroupsSpec { @@ -40,7 +41,7 @@ function atomGroupsLinear(atomTest: Atom.Predicate): Query { const { atoms, units } = structure; const unitIds = AtomSet.unitIds(atoms); const l = Atom.Location(); - const builder = AtomSet.Builder(atoms); + const builder = AtomSet.LinearBuilder(atoms); for (let i = 0, _i = unitIds.length; i < _i; i++) { const unitId = unitIds[i]; @@ -55,7 +56,7 @@ function atomGroupsLinear(atomTest: Atom.Predicate): Query { builder.commitUnit(unitId); } - return { units, atoms: builder.getSet() }; + return Structure.create(units, builder.getSet()); }; } @@ -64,7 +65,7 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A const { atoms, units } = structure; const unitIds = AtomSet.unitIds(atoms); const l = Atom.Location(); - const builder = AtomSet.Builder(atoms); + const builder = AtomSet.LinearBuilder(atoms); for (let i = 0, _i = unitIds.length; i < _i; i++) { const unitId = unitIds[i]; @@ -98,16 +99,60 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A builder.commitUnit(unitId); } - return { units, atoms: builder.getSet() }; + return Structure.create(units, builder.getSet()); }; } +class LinearGroupingBuilder { + private builders: AtomSet.Builder[] = []; + private builderMap: { [key: string]: AtomSet.Builder } = Object.create(null); + + add(key: any, unit: number, atom: number) { + let b = this.builderMap[key]; + if (!b) { + b = AtomSet.LinearBuilder(this.structure.atoms); + this.builders[this.builders.length] = b; + this.builderMap[key] = b; + } + b.add(unit, atom); + } + + private allSingletons() { + for (let i = 0, _i = this.builders.length; i < _i; i++) { + if (this.builders[i].atomCount > 1) return false; + } + return true; + } + + private singletonStructure(): Structure { + const atoms: Atom[] = Atom.createEmptyArray(this.builders.length); + for (let i = 0, _i = this.builders.length; i < _i; i++) { + atoms[i] = this.builders[i].singleton(); + } + return Structure.create(this.structure.units, AtomSet.create(atoms)); + } + + private fullSelection() { + const ret: Structure[] = []; + for (let i = 0, _i = this.builders.length; i < _i; i++) { + ret[i] = Structure.create(this.structure.units, this.builders[i].getSet()); + } + return ret; + } + + getSelection(): Selection { + const len = this.builders.length; + if (len === 0) return Selection.Empty; + if (len === 1) return Structure.create(this.structure.units, this.builders[0].getSet()); + if (this.allSingletons()) return this.singletonStructure(); + return this.fullSelection(); + } + + constructor(private structure: Structure) { } +} + function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomGroupsSpec): Query { return structure => { throw 'nyi' }; -} - -// class LinearGroupingBuilder { - -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-data/structure/query/selection.ts b/src/mol-data/structure/query/selection.ts index 48f96ec4913b3c54ae091a98ffdb564c968d57e7..57700f93130819e001a66dd4ebb1b7f051426fee 100644 --- a/src/mol-data/structure/query/selection.ts +++ b/src/mol-data/structure/query/selection.ts @@ -27,20 +27,20 @@ namespace Selection { 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) }; + return Structure.create(unionUnits(sel), 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.map<Atom, Structure>(AtomSet.atoms(sel.atoms), atoms => Structure.create(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 Structure.create(sel.units, AtomSet.atomGetAt(sel.atoms, i)); } return sel[i]; } diff --git a/src/mol-data/structure/structure/atom.ts b/src/mol-data/structure/structure/atom.ts index cdc5b1302c2922ff7efdd4c8d14824547853b360..2a5b1c0192e1f407224187ac47a5fc0515339d96 100644 --- a/src/mol-data/structure/structure/atom.ts +++ b/src/mol-data/structure/structure/atom.ts @@ -20,6 +20,8 @@ namespace Atom { export const areEqual: (a: Atom, b: Atom) => boolean = Tuple.areEqual; export const hashCode: (a: Atom) => number = Tuple.hashCode; + export function createEmptyArray(n: number): Atom[] { return new Float64Array(n) as any; } + /** All the information required to access atom properties */ export interface Location { unit: Unit, atom: number } export function Location(): Location { return { unit: {} as any, atom: 0 }; } diff --git a/src/mol-data/structure/structure/atom/set.ts b/src/mol-data/structure/structure/atom/set.ts index 93a2177949c73603dce5ba069718101c90ecb1e4..6abe38108e98014014ece3185f83d18554de0560 100644 --- a/src/mol-data/structure/structure/atom/set.ts +++ b/src/mol-data/structure/structure/atom/set.ts @@ -7,7 +7,7 @@ import { OrderedSet, SortedArray, Iterator } from 'mol-base/collections/integer' import Atom from '../atom' import * as Impl from './set/impl' -import createBuilder from './set/builder' +import createBuilder, { Builder as AtomSetBuilder } from './set/builder' /** A map-like representation of grouped atom set */ namespace AtomSet { @@ -38,8 +38,9 @@ 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 function SortedBuilder(parent: AtomSet) { return createBuilder(parent, true); } - export function Builder(parent: AtomSet) { return createBuilder(parent, false); } + export type Builder = AtomSetBuilder + export function LinearBuilder(parent: AtomSet): Builder { return createBuilder(parent, true); } + export function UnsortedBuilder(parent: AtomSet): Builder { return createBuilder(parent, false); } // TODO: bounding sphere // TODO: distance, areWithIn? diff --git a/src/mol-data/structure/structure/atom/set/builder.ts b/src/mol-data/structure/structure/atom/set/builder.ts index 2d51dc9c5f7ae2a574695d52ebc84e06854065d7..bf756939e99e700d0e504770b1d3f11eb41177b2 100644 --- a/src/mol-data/structure/structure/atom/set/builder.ts +++ b/src/mol-data/structure/structure/atom/set/builder.ts @@ -5,14 +5,17 @@ */ import AtomSet from '../set' +import Atom from '../../atom' import { OrderedSet } from 'mol-base/collections/integer' import { sortArray } from 'mol-base/collections/sort' -class Builder { +export class Builder { private keys: number[] = []; private units: number[][] = Object.create(null); private currentUnit: number[] = []; + atomCount = 0; + add(u: number, a: number) { const unit = this.units[u]; if (!!unit) { unit[unit.length] = a; } @@ -20,10 +23,11 @@ class Builder { this.units[u] = [a]; this.keys[this.keys.length] = u; } + this.atomCount++; } beginUnit() { this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; } - addToUnit(a: number) { this.currentUnit[this.currentUnit.length] = a; } + addToUnit(a: number) { this.currentUnit[this.currentUnit.length] = a; this.atomCount++; } commitUnit(u: number) { if (this.currentUnit.length === 0) return; this.keys[this.keys.length] = u; @@ -53,6 +57,11 @@ class Builder { return allEqual ? this.parent : AtomSet.create(sets); } + singleton(): Atom { + const u = this.keys[0]; + return Atom.create(u, this.units[u][0]); + } + constructor(private parent: AtomSet, private sorted: boolean) { } } diff --git a/src/mol-data/structure/structure/atom/set/impl.ts b/src/mol-data/structure/structure/atom/set/impl.ts index dbb0974826fc6cd440ec8fcdddc85350f596b741..8c0f2bcd4dfec8e3a2f57ecc92436254b19c2470 100644 --- a/src/mol-data/structure/structure/atom/set/impl.ts +++ b/src/mol-data/structure/structure/atom/set/impl.ts @@ -467,7 +467,7 @@ function unionN(sets: ArrayLike<AtomSetImpl>, eCount: { count: number }) { eCount.count = countE; if (!countN) return Empty; if (countN === sets.length) return ofAtoms(sets as ArrayLike<Atom>); - const packed = new Float64Array(countN); + const packed = Atom.createEmptyArray(countN); let offset = 0; for (let i = 0, _i = sets.length; i < _i; i++) { const s = sets[i]; diff --git a/src/mol-data/structure/structure/structure.ts b/src/mol-data/structure/structure/structure.ts index 8917e842b52a194f47f93176d566fc9eb484be2d..be0df65b59761a024b43690ce74350d164e62a40 100644 --- a/src/mol-data/structure/structure/structure.ts +++ b/src/mol-data/structure/structure/structure.ts @@ -18,6 +18,10 @@ interface Structure extends Readonly<{ namespace Structure { export const Empty = { units: {}, atoms: AtomSet.Empty }; + export function create(units: Structure['units'], atoms: AtomSet): Structure { + return { units, atoms }; + } + export function ofData(format: Format) { const models = Model.create(format); return models.map(ofModel); @@ -50,7 +54,7 @@ namespace Structure { 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 ? { units: this.units, atoms: AtomSet.create(this.atoms) } : Empty; } + getStructure(): Structure { return this.atomCount > 0 ? Structure.create(this.units, AtomSet.create(this.atoms)) : Empty; } } export function Builder(): Builder { return new BuilderImpl(); } diff --git a/src/perf-tests/sets.ts b/src/perf-tests/sets.ts index 9a3461414e8b1da7e4ed2323c85c6e6a501fd517..76d8c03c7b003c8bd6ae9d38b65ab4ea8b9bb8fb 100644 --- a/src/perf-tests/sets.ts +++ b/src/perf-tests/sets.ts @@ -200,7 +200,7 @@ export namespace Union { export namespace Build { function createSorted() { - const b = AtomSet.SortedBuilder(AtomSet.Empty); + const b = AtomSet.LinearBuilder(AtomSet.Empty); for (let i = 0; i < 10; i++) { for (let j = 0; j < 1000; j++) { b.add(i, j); @@ -210,7 +210,7 @@ export namespace Build { } function createByUnit() { - const b = AtomSet.SortedBuilder(AtomSet.Empty); + const b = AtomSet.LinearBuilder(AtomSet.Empty); for (let i = 0; i < 10; i++) { b.beginUnit(); for (let j = 0; j < 1000; j++) { diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 23300ac6a0e8545075ea87e8e25ee39a79e2f987..331b46ebfee5b7c745474826cfbe178e2fb1f3c9 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -236,7 +236,7 @@ export namespace PropertyAccess { export async function run() { const { structures, models } = await readCIF('./examples/1cbs_full.bcif'); - //const { structures, models } = await readCIF('e:/test/quick/3j3q_full.bcif'); + //const { structures, models } = await readCIF('e:/test/quick/1jj2_full.bcif'); //const { structures, models } = await readCIF('e:/test/quick/3j3q_updated.cif'); console.log('parsed');