From ac1e2b0d51f1d8f45b88f2d7d85fb449608084e8 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Mon, 14 May 2018 18:38:32 +0200 Subject: [PATCH] Query wholeResidues, includeSurroundings --- src/apps/structure-info/model.ts | 4 +- src/mol-data/generic/unique-array.ts | 3 +- .../geometry/spacegroup/construction.ts | 2 - src/mol-model/structure/query.ts | 2 + src/mol-model/structure/query/generators.ts | 12 +- src/mol-model/structure/query/modifers.ts | 44 ----- src/mol-model/structure/query/modifiers.ts | 102 ++++++++++++ .../structure/query/utils/builders.ts | 5 +- .../structure/query/utils/structure.ts | 3 +- .../structure/structure/structure.ts | 120 ++------------ src/mol-model/structure/structure/symmetry.ts | 2 + .../structure/structure/util/lookup3d.ts | 153 ++++++++++-------- .../structure/util/subset-builder.ts | 116 +++++++++++++ .../structure/util/unique-subset-builder.ts | 98 +++++++++++ src/perf-tests/structure.ts | 40 ++++- 15 files changed, 471 insertions(+), 235 deletions(-) delete mode 100644 src/mol-model/structure/query/modifers.ts create mode 100644 src/mol-model/structure/query/modifiers.ts create mode 100644 src/mol-model/structure/structure/util/subset-builder.ts create mode 100644 src/mol-model/structure/structure/util/unique-subset-builder.ts diff --git a/src/apps/structure-info/model.ts b/src/apps/structure-info/model.ts index c6d606cfb..13c6ed442 100644 --- a/src/apps/structure-info/model.ts +++ b/src/apps/structure-info/model.ts @@ -119,9 +119,9 @@ export function printUnits(structure: Structure) { const size = OrderedSet.size(elements); if (Unit.isAtomic(l.unit)) { - console.log(`Atomic unit ${unit.id}: ${size} elements`); + console.log(`Atomic unit ${unit.id} ${unit.conformation.operator.name}: ${size} elements`); } else if (Unit.isCoarse(l.unit)) { - console.log(`Coarse unit ${unit.id} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`); + console.log(`Coarse unit ${unit.id} ${unit.conformation.operator.name} (${Unit.isSpheres(l.unit) ? 'spheres' : 'gaussians'}): ${size} elements.`); const props = Queries.props.coarse; const seq = l.unit.model.sequence; diff --git a/src/mol-data/generic/unique-array.ts b/src/mol-data/generic/unique-array.ts index 5a5caa501..dad669395 100644 --- a/src/mol-data/generic/unique-array.ts +++ b/src/mol-data/generic/unique-array.ts @@ -15,9 +15,10 @@ namespace UniqueArray { } export function add<K, T>({ keys, array }: UniqueArray<K, T>, key: K, value: T) { - if (keys.has(key)) return; + if (keys.has(key)) return false; keys.add(key); array[array.length] = value; + return true; } } diff --git a/src/mol-math/geometry/spacegroup/construction.ts b/src/mol-math/geometry/spacegroup/construction.ts index 0492fc297..2acdb5287 100644 --- a/src/mol-math/geometry/spacegroup/construction.ts +++ b/src/mol-math/geometry/spacegroup/construction.ts @@ -87,8 +87,6 @@ namespace Spacegroup { export function getSymmetryOperator(spacegroup: Spacegroup, index: number, i: number, j: number, k: number): SymmetryOperator { const operator = updateOperatorMatrix(spacegroup, index, i, j, k, Mat4.zero()); - console.log(Mat4.makeTable(operator)); - console.log({ index, i, j, k }); return SymmetryOperator.create(`${index + 1}_${5 + i}${5 + j}${5 + k}`, operator, Vec3.create(i, j, k)); } diff --git a/src/mol-model/structure/query.ts b/src/mol-model/structure/query.ts index 55dff3a8e..b5886cbda 100644 --- a/src/mol-model/structure/query.ts +++ b/src/mol-model/structure/query.ts @@ -7,11 +7,13 @@ import Selection from './query/selection' import Query from './query/query' import * as generators from './query/generators' +import * as modifiers from './query/modifiers' import props from './query/properties' import pred from './query/predicates' export const Queries = { generators, + modifiers, props, pred } diff --git a/src/mol-model/structure/query/generators.ts b/src/mol-model/structure/query/generators.ts index 8bbc8a17d..0650a17ae 100644 --- a/src/mol-model/structure/query/generators.ts +++ b/src/mol-model/structure/query/generators.ts @@ -50,6 +50,7 @@ function atomGroupsLinear(atomTest: Element.Predicate): Query.Provider { const l = Element.Location(); const builder = structure.subsetBuilder(true); + let progress = 0; for (const unit of units) { l.unit = unit; const elements = unit.elements; @@ -61,7 +62,8 @@ function atomGroupsLinear(atomTest: Element.Predicate): Query.Provider { } builder.commitUnit(); - if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: units.length }); + progress++; + if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: progress, max: units.length }); } return Selection.Singletons(structure, builder.getStructure()); @@ -74,6 +76,7 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A const l = Element.Location(); const builder = structure.subsetBuilder(true); + let progress = 0; for (const unit of units) { if (unit.kind !== Unit.Kind.Atomic) continue; @@ -107,7 +110,8 @@ function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: A } builder.commitUnit(); - if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: units.length }); + progress++; + if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: progress, max: units.length }); } return Selection.Singletons(structure, builder.getStructure()); @@ -120,6 +124,7 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group const l = Element.Location(); const builder = new LinearGroupingBuilder(structure); + let progress = 0; for (const unit of units) { if (unit.kind !== Unit.Kind.Atomic) continue; @@ -149,7 +154,8 @@ function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, group } } - if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: units.length }); + progress++; + if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: progress, max: units.length }); } return builder.getSelection(); diff --git a/src/mol-model/structure/query/modifers.ts b/src/mol-model/structure/query/modifers.ts deleted file mode 100644 index a7868906d..000000000 --- a/src/mol-model/structure/query/modifers.ts +++ /dev/null @@ -1,44 +0,0 @@ -// /** -// * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. -// * -// * @author David Sehnal <david.sehnal@gmail.com> -// */ - -// import Query from './query' -// import Selection from './selection' -// import P from './properties' -// import { Element, Unit } from '../structure' -// import { OrderedSet, Segmentation } from 'mol-data/int' -// import { LinearGroupingBuilder } from './utils/builders'; - -// export function wholeResidues(query: Query, isFlat: boolean): Query.Provider { -// return async (structure, ctx) => { -// const selection = query(structure).runAsChild(ctx); -// const { units } = structure; -// const l = Element.Location(); -// const builder = structure.subsetBuilder(true); - -// for (const unit of units) { -// l.unit = unit; -// const elements = unit.elements; - -// builder.beginUnit(unit.id); -// for (let j = 0, _j = elements.length; j < _j; j++) { -// l.element = elements[j]; -// if (atomTest(l)) builder.addElement(l.element); -// } -// builder.commitUnit(); - -// if (ctx.shouldUpdate) await ctx.update({ message: 'Atom Groups', current: 0, max: units.length }); -// } - -// return Selection.Singletons(structure, builder.getStructure()); -// }; -// } - -// export interface IncludeSurroundingsParams { -// selection: Selection, -// radius: number, -// atomRadius?: number, -// wholeResidues?: boolean -// } \ No newline at end of file diff --git a/src/mol-model/structure/query/modifiers.ts b/src/mol-model/structure/query/modifiers.ts new file mode 100644 index 000000000..e66bbab42 --- /dev/null +++ b/src/mol-model/structure/query/modifiers.ts @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { Segmentation } from 'mol-data/int'; +import { RuntimeContext } from 'mol-task'; +import { Structure, Unit } from '../structure'; +import Query from './query'; +import Selection from './selection'; +import { UniqueStructuresBuilder } from './utils/builders'; +import { StructureUniqueSubsetBuilder } from '../structure/util/unique-subset-builder'; + +function getWholeResidues(ctx: RuntimeContext, source: Structure, structure: Structure) { + const builder = source.subsetBuilder(true); + for (const unit of structure.units) { + if (unit.kind !== Unit.Kind.Atomic) { + // just copy non-atomic units. + builder.setUnit(unit.id, unit.elements); + continue; + } + + const { residueSegments } = unit.model.atomicHierarchy; + + const elements = unit.elements; + builder.beginUnit(unit.id); + const residuesIt = Segmentation.transientSegments(residueSegments, elements); + while (residuesIt.hasNext) { + const rI = residuesIt.move().index; + for (let j = residueSegments.segments[rI], _j = residueSegments.segments[rI + 1]; j < _j; j++) { + builder.addElement(j); + } + } + builder.commitUnit(); + } + return builder.getStructure(); +} + +export function wholeResidues(query: Query.Provider, isFlat: boolean): Query.Provider { + return async (structure, ctx) => { + const inner = await query(structure, ctx); + if (Selection.isSingleton(inner)) { + return Selection.Singletons(structure, getWholeResidues(ctx, structure, inner.structure)); + } else { + const builder = new UniqueStructuresBuilder(structure); + let progress = 0; + for (const s of inner.structures) { + builder.add(getWholeResidues(ctx, structure, s)); + progress++; + if (ctx.shouldUpdate) await ctx.update({ message: 'Whole Residues', current: progress, max: inner.structures.length }); + } + return builder.getSelection(); + } + }; +} + + +// export function groupBy() ... + +export interface IncludeSurroundingsParams { + radius: number, + // atomRadius?: Element.Property<number>, + wholeResidues?: boolean +} + +async function getIncludeSurroundings(ctx: RuntimeContext, source: Structure, structure: Structure, params: IncludeSurroundingsParams) { + const builder = new StructureUniqueSubsetBuilder(source); + const lookup = source.lookup3d; + const r = params.radius; + + let progress = 0; + + for (const unit of structure.units) { + const { x, y, z } = unit.conformation; + const elements = unit.elements; + for (let i = 0, _i = elements.length; i < _i; i++) { + const e = elements[i]; + lookup.findIntoBuilder(x(e), y(e), z(e), r, builder); + } + + progress++; + if (progress % 2500 === 0 && ctx.shouldUpdate) await ctx.update({ message: 'Include Surroudnings', isIndeterminate: true }); + } + if (!!params.wholeResidues) return getWholeResidues(ctx, source, builder.getStructure()) + return builder.getStructure(); +} + +export function includeSurroundings(query: Query.Provider, params: IncludeSurroundingsParams): Query.Provider { + return async (structure, ctx) => { + const inner = await query(structure, ctx); + if (Selection.isSingleton(inner)) { + return Selection.Singletons(structure, await getIncludeSurroundings(ctx, structure, inner.structure, params)); + } else { + const builder = new UniqueStructuresBuilder(structure); + for (const s of inner.structures) { + builder.add(await getIncludeSurroundings(ctx, structure, s, params)); + } + return builder.getSelection(); + } + }; +} \ No newline at end of file diff --git a/src/mol-model/structure/query/utils/builders.ts b/src/mol-model/structure/query/utils/builders.ts index fa9020984..6e1d6d7cf 100644 --- a/src/mol-model/structure/query/utils/builders.ts +++ b/src/mol-model/structure/query/utils/builders.ts @@ -8,6 +8,7 @@ import { Element, Structure } from '../../structure'; import Selection from '../selection'; import { HashSet } from 'mol-data/generic'; import { structureUnion } from './structure'; +import { StructureSubsetBuilder } from '../../structure/util/subset-builder'; export class UniqueStructuresBuilder { private set = HashSet(Structure.hashCode, Structure.areEqual); @@ -32,8 +33,8 @@ export class UniqueStructuresBuilder { } export class LinearGroupingBuilder { - private builders: Structure.SubsetBuilder[] = []; - private builderMap = new Map<string, Structure.SubsetBuilder>(); + private builders: StructureSubsetBuilder[] = []; + private builderMap = new Map<string, StructureSubsetBuilder>(); add(key: any, unit: number, element: number) { let b = this.builderMap.get(key); diff --git a/src/mol-model/structure/query/utils/structure.ts b/src/mol-model/structure/query/utils/structure.ts index 903a5d0cb..f30fd20e6 100644 --- a/src/mol-model/structure/query/utils/structure.ts +++ b/src/mol-model/structure/query/utils/structure.ts @@ -6,6 +6,7 @@ import { Structure, Unit } from '../../structure' import { SortedArray } from 'mol-data/int'; +import { StructureSubsetBuilder } from '../../structure/util/subset-builder'; export function structureUnion(source: Structure, structures: Structure[]) { if (structures.length === 0) return Structure.Empty; @@ -35,7 +36,7 @@ export function structureUnion(source: Structure, structures: Structure[]) { return builder.getStructure(); } -function buildUnion(this: Structure.SubsetBuilder, elements: SortedArray, id: number) { +function buildUnion(this: StructureSubsetBuilder, elements: SortedArray, id: number) { this.setUnit(id, elements); } diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index c0b39c2fa..79b21a339 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -8,12 +8,12 @@ import { IntMap, SortedArray, Iterator } from 'mol-data/int' import { UniqueArray } from 'mol-data/generic' import { SymmetryOperator } from 'mol-math/geometry/symmetry-operator' import { Model } from '../model' -import { sortArray, sort, arraySwap, hash1 } from 'mol-data/util'; +import { sort, arraySwap, hash1 } from 'mol-data/util'; import Element from './element' import Unit from './unit' import { StructureLookup3D } from './util/lookup3d'; -import StructureSymmetry from './symmetry'; import { CoarseElements } from '../model/properties/coarse'; +import { StructureSubsetBuilder } from './util/subset-builder'; class Structure { readonly unitMap: IntMap<Unit>; @@ -23,7 +23,7 @@ class Structure { private _hashCode = 0; subsetBuilder(isSorted: boolean) { - return new Structure.SubsetBuilder(this, isSorted); + return new StructureSubsetBuilder(this, isSorted); } get hashCode() { @@ -55,7 +55,7 @@ class Structure { private _lookup3d?: StructureLookup3D = void 0; get lookup3d() { if (this._lookup3d) return this._lookup3d; - this._lookup3d = StructureLookup3D.create(this); + this._lookup3d = new StructureLookup3D(this); return this._lookup3d; } @@ -141,106 +141,6 @@ namespace Structure { export function Builder() { return new StructureBuilder(); } - export class SubsetBuilder { - private ids: number[] = []; - private unitMap = IntMap.Mutable<number[]>(); - private parentId = -1; - private currentUnit: number[] = []; - elementCount = 0; - - addToUnit(parentId: number, e: number) { - const unit = this.unitMap.get(parentId); - if (!!unit) { unit[unit.length] = e; } - else { - this.unitMap.set(parentId, [e]); - this.ids[this.ids.length] = parentId; - } - this.elementCount++; - } - - beginUnit(parentId: number) { - this.parentId = parentId; - this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; - } - - addElement(e: number) { - this.currentUnit[this.currentUnit.length] = e; - this.elementCount++; - } - - commitUnit() { - if (this.currentUnit.length === 0) return; - this.ids[this.ids.length] = this.parentId; - this.unitMap.set(this.parentId, this.currentUnit); - this.parentId = -1; - } - - setUnit(parentId: number, elements: ArrayLike<number>) { - this.ids[this.ids.length] = parentId; - this.unitMap.set(parentId, elements as number[]); - this.elementCount += elements.length; - } - - private _getStructure(deduplicateElements: boolean): Structure { - if (this.isEmpty) return Structure.Empty; - - const newUnits: Unit[] = []; - sortArray(this.ids); - - const symmGroups = StructureSymmetry.UnitEquivalenceBuilder(); - - for (let i = 0, _i = this.ids.length; i < _i; i++) { - const id = this.ids[i]; - const parent = this.parent.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. - if (unit.length === parent.elements.length) { - newUnits[newUnits.length] = parent; - symmGroups.add(parent.id, parent); - continue; - } - - if (!this.isSorted && !sorted && l > 1) sortArray(unit); - - let child = parent.getChild(SortedArray.ofSortedArray(unit)); - const pivot = symmGroups.add(child.id, child); - if (child !== pivot) child = pivot.applyOperator(child.id, child.conformation.operator, true); - newUnits[newUnits.length] = child; - } - - 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); - location.element = this.unitMap.get(id)[0]; - } - - get isEmpty() { - return this.elementCount === 0; - } - - constructor(private parent: Structure, private isSorted: boolean) { - - } - } - export function getModels(s: Structure) { const { units } = s; const arr = UniqueArray.create<Model['id'], Model>(); @@ -282,18 +182,18 @@ namespace Structure { private current = Element.Location(); private unitIndex = 0; private elements: SortedArray; - private len = 0; - private idx = 0; + private maxIdx = 0; + private idx = -1; hasNext: boolean; move(): Element.Location { - this.current.element = this.elements[this.idx]; this.advance(); + this.current.element = this.elements[this.idx]; return this.current; } private advance() { - if (this.idx < this.len - 1) { + if (this.idx < this.maxIdx) { this.idx++; return; } @@ -307,14 +207,14 @@ namespace Structure { this.current.unit = this.structure.units[this.unitIndex]; this.elements = this.current.unit.elements; - this.len = this.elements.length; + this.maxIdx = this.elements.length - 1; } constructor(private structure: Structure) { this.hasNext = structure.elementCount > 0; if (this.hasNext) { this.elements = structure.units[0].elements; - this.len = this.elements.length; + this.maxIdx = this.elements.length - 1; this.current.unit = structure.units[0]; } } diff --git a/src/mol-model/structure/structure/symmetry.ts b/src/mol-model/structure/structure/symmetry.ts index 4a0abaf78..0a912a4ab 100644 --- a/src/mol-model/structure/structure/symmetry.ts +++ b/src/mol-model/structure/structure/symmetry.ts @@ -43,6 +43,8 @@ namespace StructureSymmetry { }); } + // TODO: build symmetry mates within radius + export function buildSymmetryRange(structure: Structure, ijkMin: Vec3, ijkMax: Vec3) { return Task.create('Build Assembly', async ctx => { const models = Structure.getModels(structure); diff --git a/src/mol-model/structure/structure/util/lookup3d.ts b/src/mol-model/structure/structure/util/lookup3d.ts index f3056afdc..bee1c7c03 100644 --- a/src/mol-model/structure/structure/util/lookup3d.ts +++ b/src/mol-model/structure/structure/util/lookup3d.ts @@ -10,87 +10,102 @@ import { Lookup3D, GridLookup3D, Result, Box3D, Sphere3D } from 'mol-math/geomet import { Vec3 } from 'mol-math/linear-algebra'; import { computeStructureBoundary } from './boundary'; import { OrderedSet } from 'mol-data/int'; - -interface StructureLookup3D extends Lookup3D<Element> {} - -namespace StructureLookup3D { - class Impl implements StructureLookup3D { - private unitLookup: Lookup3D; - private result = Result.create<Element>(); - private pivot = Vec3.zero(); - - find(x: number, y: number, z: number, radius: number): Result<Element> { - Result.reset(this.result); - const { units } = this.structure; - const closeUnits = this.unitLookup.find(x, y, z, radius); - if (closeUnits.count === 0) return this.result; - - for (let t = 0, _t = closeUnits.count; t < _t; t++) { - const unit = units[closeUnits.indices[t]]; - Vec3.set(this.pivot, x, y, z); - if (!unit.conformation.operator.isIdentity) { - Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse); - } - const unitLookup = unit.lookup3d; - const groupResult = unitLookup.find(this.pivot[0], this.pivot[1], this.pivot[2], radius); - for (let j = 0, _j = groupResult.count; j < _j; j++) { - Result.add(this.result, Element.create(unit.id, groupResult.indices[j]), groupResult.squaredDistances[j]); - } +import { StructureUniqueSubsetBuilder } from './unique-subset-builder'; + +export class StructureLookup3D implements Lookup3D<Element> { + private unitLookup: Lookup3D; + private result = Result.create<Element>(); + private pivot = Vec3.zero(); + + find(x: number, y: number, z: number, radius: number): Result<Element> { + Result.reset(this.result); + const { units } = this.structure; + const closeUnits = this.unitLookup.find(x, y, z, radius); + if (closeUnits.count === 0) return this.result; + + for (let t = 0, _t = closeUnits.count; t < _t; t++) { + const unit = units[closeUnits.indices[t]]; + Vec3.set(this.pivot, x, y, z); + if (!unit.conformation.operator.isIdentity) { + Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse); + } + const unitLookup = unit.lookup3d; + const groupResult = unitLookup.find(this.pivot[0], this.pivot[1], this.pivot[2], radius); + for (let j = 0, _j = groupResult.count; j < _j; j++) { + Result.add(this.result, Element.create(unit.id, groupResult.indices[j]), groupResult.squaredDistances[j]); } - - return this.result; } - check(x: number, y: number, z: number, radius: number): boolean { - const { units } = this.structure; - const closeUnits = this.unitLookup.find(x, y, z, radius); - if (closeUnits.count === 0) return false; - - for (let t = 0, _t = closeUnits.count; t < _t; t++) { - const unit = units[closeUnits.indices[t]]; - Vec3.set(this.pivot, x, y, z); - if (!unit.conformation.operator.isIdentity) { - Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse); - } - const groupLookup = unit.lookup3d; - if (groupLookup.check(this.pivot[0], this.pivot[1], this.pivot[2], radius)) return true; + return this.result; + } + + findIntoBuilder(x: number, y: number, z: number, radius: number, builder: StructureUniqueSubsetBuilder) { + const { units } = this.structure; + const closeUnits = this.unitLookup.find(x, y, z, radius); + if (closeUnits.count === 0) return; + + for (let t = 0, _t = closeUnits.count; t < _t; t++) { + const unit = units[closeUnits.indices[t]]; + Vec3.set(this.pivot, x, y, z); + if (!unit.conformation.operator.isIdentity) { + Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse); } + const unitLookup = unit.lookup3d; + const groupResult = unitLookup.find(this.pivot[0], this.pivot[1], this.pivot[2], radius); + if (groupResult.count === 0) continue; + + const elements = unit.elements; + builder.beginUnit(unit.id); + for (let j = 0, _j = groupResult.count; j < _j; j++) { + builder.addElement(elements[groupResult.indices[j]]); + } + builder.commitUnit(); + } + } + + check(x: number, y: number, z: number, radius: number): boolean { + const { units } = this.structure; + const closeUnits = this.unitLookup.find(x, y, z, radius); + if (closeUnits.count === 0) return false; - return false; + for (let t = 0, _t = closeUnits.count; t < _t; t++) { + const unit = units[closeUnits.indices[t]]; + Vec3.set(this.pivot, x, y, z); + if (!unit.conformation.operator.isIdentity) { + Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse); + } + const groupLookup = unit.lookup3d; + if (groupLookup.check(this.pivot[0], this.pivot[1], this.pivot[2], radius)) return true; } - boundary: { box: Box3D; sphere: Sphere3D; }; + return false; + } - constructor(private structure: Structure) { - const { units } = structure; - const unitCount = units.length; - const xs = new Float32Array(unitCount); - const ys = new Float32Array(unitCount); - const zs = new Float32Array(unitCount); - const radius = new Float32Array(unitCount); + boundary: { box: Box3D; sphere: Sphere3D; }; - const center = Vec3.zero(); - for (let i = 0; i < unitCount; i++) { - const unit = units[i]; - const lookup = unit.lookup3d; - const s = lookup.boundary.sphere; + constructor(private structure: Structure) { + const { units } = structure; + const unitCount = units.length; + const xs = new Float32Array(unitCount); + const ys = new Float32Array(unitCount); + const zs = new Float32Array(unitCount); + const radius = new Float32Array(unitCount); - Vec3.transformMat4(center, s.center, unit.conformation.operator.matrix); + const center = Vec3.zero(); + for (let i = 0; i < unitCount; i++) { + const unit = units[i]; + const lookup = unit.lookup3d; + const s = lookup.boundary.sphere; - xs[i] = center[0]; - ys[i] = center[1]; - zs[i] = center[2]; - radius[i] = s.radius; - } + Vec3.transformMat4(center, s.center, unit.conformation.operator.matrix); - this.unitLookup = GridLookup3D({ x: xs, y: ys, z: zs, radius, indices: OrderedSet.ofBounds(0, unitCount) }); - this.boundary = computeStructureBoundary(structure); + xs[i] = center[0]; + ys[i] = center[1]; + zs[i] = center[2]; + radius[i] = s.radius; } - } - export function create(s: Structure): StructureLookup3D { - return new Impl(s); + this.unitLookup = GridLookup3D({ x: xs, y: ys, z: zs, radius, indices: OrderedSet.ofBounds(0, unitCount) }); + this.boundary = computeStructureBoundary(structure); } -} - -export { StructureLookup3D } \ No newline at end of file +} \ No newline at end of file diff --git a/src/mol-model/structure/structure/util/subset-builder.ts b/src/mol-model/structure/structure/util/subset-builder.ts new file mode 100644 index 000000000..7c1437742 --- /dev/null +++ b/src/mol-model/structure/structure/util/subset-builder.ts @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { IntMap, SortedArray } from 'mol-data/int'; +import { sortArray } from 'mol-data/util'; +import Element from '../element'; +import StructureSymmetry from '../symmetry'; +import Unit from '../unit'; +import Structure from '../structure'; + +export class StructureSubsetBuilder { + private ids: number[] = []; + private unitMap = IntMap.Mutable<number[]>(); + private parentId = -1; + private currentUnit: number[] = []; + elementCount = 0; + + addToUnit(parentId: number, e: number) { + const unit = this.unitMap.get(parentId); + if (!!unit) { unit[unit.length] = e; } + else { + this.unitMap.set(parentId, [e]); + this.ids[this.ids.length] = parentId; + } + this.elementCount++; + } + + beginUnit(parentId: number) { + this.parentId = parentId; + this.currentUnit = this.currentUnit.length > 0 ? [] : this.currentUnit; + } + + addElement(e: number) { + this.currentUnit[this.currentUnit.length] = e; + this.elementCount++; + } + + commitUnit() { + if (this.currentUnit.length === 0) return; + this.ids[this.ids.length] = this.parentId; + this.unitMap.set(this.parentId, this.currentUnit); + this.parentId = -1; + } + + setUnit(parentId: number, elements: ArrayLike<number>) { + this.ids[this.ids.length] = parentId; + this.unitMap.set(parentId, elements as number[]); + this.elementCount += elements.length; + } + + private _getStructure(deduplicateElements: boolean): Structure { + if (this.isEmpty) return Structure.Empty; + + const newUnits: Unit[] = []; + sortArray(this.ids); + + const symmGroups = StructureSymmetry.UnitEquivalenceBuilder(); + + for (let i = 0, _i = this.ids.length; i < _i; i++) { + const id = this.ids[i]; + const parent = this.parent.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. + if (unit.length === parent.elements.length) { + newUnits[newUnits.length] = parent; + symmGroups.add(parent.id, parent); + continue; + } + + if (!this.isSorted && !sorted && l > 1) sortArray(unit); + + let child = parent.getChild(SortedArray.ofSortedArray(unit)); + const pivot = symmGroups.add(child.id, child); + if (child !== pivot) child = pivot.applyOperator(child.id, child.conformation.operator, true); + newUnits[newUnits.length] = child; + } + + return Structure.create(newUnits); + } + + getStructure() { + return this._getStructure(false); + } + + getStructureDeduplicate() { + return this._getStructure(true); + } + + setSingletonLocation(location: Element.Location) { + const id = this.ids[0]; + location.unit = this.parent.unitMap.get(id); + location.element = this.unitMap.get(id)[0]; + } + + get isEmpty() { + return this.elementCount === 0; + } + + constructor(private parent: Structure, private isSorted: boolean) { + + } +} diff --git a/src/mol-model/structure/structure/util/unique-subset-builder.ts b/src/mol-model/structure/structure/util/unique-subset-builder.ts new file mode 100644 index 000000000..bc18e6519 --- /dev/null +++ b/src/mol-model/structure/structure/util/unique-subset-builder.ts @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { IntMap, SortedArray } from 'mol-data/int'; +import { sortArray } from 'mol-data/util'; +import StructureSymmetry from '../symmetry'; +import Unit from '../unit'; +import Structure from '../structure'; +import { UniqueArray } from 'mol-data/generic'; + +type UArray = UniqueArray<number, number> + +export class StructureUniqueSubsetBuilder { + private ids: number[] = []; + private unitMap = IntMap.Mutable<UArray>(); + private parentId = -1; + private currentUnit: UArray = UniqueArray.create(); + elementCount = 0; + + addToUnit(parentId: number, e: number) { + const unit = this.unitMap.get(parentId); + if (!!unit) { + if (UniqueArray.add(unit, e, e)) this.elementCount++; + } + else { + const arr: UArray = UniqueArray.create(); + UniqueArray.add(arr, e, e); + this.unitMap.set(parentId, arr); + this.ids[this.ids.length] = parentId; + this.elementCount++; + } + } + + beginUnit(parentId: number) { + this.parentId = parentId; + if (this.unitMap.has(parentId)) { + this.currentUnit = this.unitMap.get(parentId); + } else { + this.currentUnit = this.currentUnit.array.length > 0 ? UniqueArray.create() : this.currentUnit; + } + } + + addElement(e: number) { + if (UniqueArray.add(this.currentUnit, e, e)) this.elementCount++; + } + + commitUnit() { + if (this.currentUnit.array.length === 0 || this.unitMap.has(this.parentId)) return; + this.ids[this.ids.length] = this.parentId; + this.unitMap.set(this.parentId, this.currentUnit); + this.parentId = -1; + } + + getStructure(): Structure { + if (this.isEmpty) return Structure.Empty; + + const newUnits: Unit[] = []; + sortArray(this.ids); + + const symmGroups = StructureSymmetry.UnitEquivalenceBuilder(); + + for (let i = 0, _i = this.ids.length; i < _i; i++) { + const id = this.ids[i]; + const parent = this.parent.unitMap.get(id); + + let unit: ArrayLike<number> = this.unitMap.get(id).array; + + const l = unit.length; + + // if the length is the same, just copy the old unit. + if (unit.length === parent.elements.length) { + newUnits[newUnits.length] = parent; + symmGroups.add(parent.id, parent); + continue; + } + + if (l > 1) sortArray(unit); + + let child = parent.getChild(SortedArray.ofSortedArray(unit)); + const pivot = symmGroups.add(child.id, child); + if (child !== pivot) child = pivot.applyOperator(child.id, child.conformation.operator, true); + newUnits[newUnits.length] = child; + } + + return Structure.create(newUnits); + } + + get isEmpty() { + return this.elementCount === 0; + } + + constructor(private parent: Structure) { + + } +} diff --git a/src/perf-tests/structure.ts b/src/perf-tests/structure.ts index 2ad787c4a..b2fea5b98 100644 --- a/src/perf-tests/structure.ts +++ b/src/perf-tests/structure.ts @@ -16,6 +16,7 @@ import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry, import to_mmCIF from 'mol-model/structure/export/mmcif' import { Vec3 } from 'mol-math/linear-algebra'; +//import { printUnits } from 'apps/structure-info/model'; //import { EquivalenceClasses } from 'mol-data/util'; require('util.promisify').shim(); @@ -315,6 +316,42 @@ export namespace PropertyAccess { console.log('exported'); } + export async function testIncludeSurroundings(id: string, s: Structure) { + //const a = s; + console.time('symmetry') + const a = await StructureSymmetry.buildSymmetryRange(s, Vec3.create(-2, -2, -2), Vec3.create(2, 2, 2)).run(); + //console.log(printUnits(a)); + + const auth_comp_id = Q.props.residue.auth_comp_id, op = Q.props.unit.operator_name; + //const q1 = Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'REA' }); + const q1 = Q.modifiers.includeSurroundings(Q.generators.atoms({ + chainTest: l => op(l) === '1_555', + residueTest: l => auth_comp_id(l) === 'REA' + }), { + radius: 5, + wholeResidues: true + }); + const surr = Selection.unionStructure(await query(Query(q1), a)); + console.timeEnd('symmetry') + + // for (const u of surr.units) { + // const { atomId } = u.model.atomicConformation; + // console.log(`${u.id}, ${u.conformation.operator.name}`); + // for (let i = 0; i < u.elements.length; i++) { + // console.log(` ${atomId.value(u.elements[i])}`); + // } + // } + + // const it = surr.elementLocations(); + // while (it.hasNext) { + // const e = it.move(); + // console.log(`${Q.props.unit.operator_name(e)} ${Q.props.atom.id(e)}`); + // } + //fs.writeFileSync(`${DATA_DIR}/${id}_surr.bcif`, to_mmCIF(id, a, true)); + fs.writeFileSync(`${DATA_DIR}/${id}_surr.cif`, to_mmCIF(id, surr, false)); + console.log('exported'); + } + // export async function testGrouping(structure: Structure) { // const { elements, units } = await Run(Symmetry.buildAssembly(structure, '1')); // console.log('grouping', units.length); @@ -354,7 +391,8 @@ export namespace PropertyAccess { // console.log(mmcif.pdbx_struct_oper_list.vector.toArray()); //await testAssembly('1hrv', structures[0]); - await testSymmetry('1cbs', structures[0]); + //await testSymmetry('1cbs', structures[0]); + await testIncludeSurroundings('1cbs', structures[0]); // throw ''; // console.log(models[0].symmetry.assemblies); -- GitLab