-
David Sehnal authoredDavid Sehnal authored
generators.ts 8.84 KiB
/**
* Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author David Sehnal <david.sehnal@gmail.com>
*/
import { StructureQuery } from '../query'
import { StructureSelection } from '../selection'
import { Unit, StructureProperties as P } from '../../structure'
import { Segmentation, SortedArray } from 'mol-data/int'
import { LinearGroupingBuilder } from '../utils/builders';
import { QueryPredicate, QueryFn, QueryContextView } from '../context';
import { UnitRing } from '../../structure/unit/rings';
import Structure from '../../structure/structure';
import { ElementIndex } from '../../model';
import { UniqueArray } from 'mol-data/generic';
import { structureSubtract } from '../utils/structure-set';
export const none: StructureQuery = ctx => StructureSelection.Sequence(ctx.inputStructure, []);
export const all: StructureQuery = ctx => StructureSelection.Singletons(ctx.inputStructure, ctx.inputStructure);
export interface AtomsQueryParams {
entityTest: QueryPredicate,
chainTest: QueryPredicate,
residueTest: QueryPredicate,
atomTest: QueryPredicate,
groupBy: QueryFn
}
export function residues(params?: Partial<AtomsQueryParams>) { return atoms({ ...params, groupBy: ctx => P.residue.key(ctx.element) }); }
export function chains(params?: Partial<AtomsQueryParams>) { return atoms({ ...params, groupBy: ctx => P.chain.key(ctx.element) }); }
function _true(ctx: QueryContextView) { return true; }
function _zero(ctx: QueryContextView) { return 0; }
export function atoms(params?: Partial<AtomsQueryParams>): StructureQuery {
if (!params || (!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy)) return all;
if (!!params.atomTest && !params.residueTest && !params.chainTest && !params.entityTest && !params.groupBy) return atomGroupsLinear(params.atomTest);
const normalized: AtomsQueryParams = {
entityTest: params.entityTest || _true,
chainTest: params.chainTest || _true,
residueTest: params.residueTest || _true,
atomTest: params.atomTest || _true,
groupBy: params.groupBy || _zero,
};
if (!params.groupBy) return atomGroupsSegmented(normalized)
return atomGroupsGrouped(normalized);
}
function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery {
return ctx => {
const { inputStructure } = ctx;
const { units } = inputStructure;
const l = ctx.pushCurrentElement();
const builder = inputStructure.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(ctx)) builder.addElement(l.element);
}
builder.commitUnit();
ctx.throwIfTimedOut();
}
ctx.popCurrentElement();
return StructureSelection.Singletons(inputStructure, builder.getStructure());
};
}
function atomGroupsSegmented({ entityTest, chainTest, residueTest, atomTest }: AtomsQueryParams): StructureQuery {
return ctx => {
const { inputStructure } = ctx;
const { units } = inputStructure;
const l = ctx.pushCurrentElement();
const builder = inputStructure.subsetBuilder(true);
for (const unit of units) {
if (unit.kind !== Unit.Kind.Atomic) continue;
l.unit = unit;
const elements = unit.elements;
builder.beginUnit(unit.id);
const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
while (chainsIt.hasNext) {
const chainSegment = chainsIt.move();
l.element = elements[chainSegment.start];
// test entity and chain
if (!entityTest(ctx) || !chainTest(ctx)) continue;
residuesIt.setSegment(chainSegment);
while (residuesIt.hasNext) {
const residueSegment = residuesIt.move();
l.element = elements[residueSegment.start];
// test residue
if (!residueTest(ctx)) continue;
for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
l.element = elements[j];
if (atomTest(ctx)) {
builder.addElement(l.element);
}
}
}
}
builder.commitUnit();
ctx.throwIfTimedOut();
}
ctx.popCurrentElement();
return StructureSelection.Singletons(inputStructure, builder.getStructure());
};
}
function atomGroupsGrouped({ entityTest, chainTest, residueTest, atomTest, groupBy }: AtomsQueryParams): StructureQuery {
return ctx => {
const { inputStructure } = ctx;
const { units } = inputStructure;
const l = ctx.pushCurrentElement();
const builder = new LinearGroupingBuilder(inputStructure);
for (const unit of units) {
if (unit.kind !== Unit.Kind.Atomic) continue;
l.unit = unit;
const elements = unit.elements;
const chainsIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, elements);
const residuesIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements);
while (chainsIt.hasNext) {
const chainSegment = chainsIt.move();
l.element = elements[chainSegment.start];
// test entity and chain
if (!entityTest(ctx) || !chainTest(ctx)) continue;
residuesIt.setSegment(chainSegment);
while (residuesIt.hasNext) {
const residueSegment = residuesIt.move();
l.element = elements[residueSegment.start];
// test residue
if (!residueTest(ctx)) continue;
for (let j = residueSegment.start, _j = residueSegment.end; j < _j; j++) {
l.element = elements[j];
if (atomTest(ctx)) builder.add(groupBy(ctx), unit.id, l.element);
}
}
}
ctx.throwIfTimedOut();
}
ctx.popCurrentElement();
return builder.getSelection();
};
}
function getRingStructure(unit: Unit.Atomic, ring: UnitRing) {
const elements = new Int32Array(ring.length) as any as ElementIndex[];
for (let i = 0, _i = ring.length; i < _i; i++) elements[i] = unit.elements[ring[i]];
return Structure.create([unit.getChild(SortedArray.ofSortedArray(elements))])
}
export function rings(fingerprints?: ArrayLike<UnitRing.Fingerprint>): StructureQuery {
return ctx => {
const { units } = ctx.inputStructure;
let ret = StructureSelection.LinearBuilder(ctx.inputStructure);
if (!fingerprints || fingerprints.length === 0) {
for (const u of units) {
if (!Unit.isAtomic(u)) continue;
for (const r of u.rings.all) {
ret.add(getRingStructure(u, r));
}
}
} else {
const uniqueFps = UniqueArray.create<UnitRing.Fingerprint, UnitRing.Fingerprint>();
for (let i = 0; i < fingerprints.length; i++) UniqueArray.add(uniqueFps, fingerprints[i], fingerprints[i]);
for (const u of units) {
if (!Unit.isAtomic(u)) continue;
const rings = u.rings;
for (const fp of uniqueFps.array) {
if (!rings.byFingerprint.has(fp)) continue;
for (const r of rings.byFingerprint.get(fp)!) {
ret.add(getRingStructure(u, rings.all[r]));
}
}
}
}
return ret.getSelection();
}
}
export function querySelection(selection: StructureQuery, query: StructureQuery, inComplement: boolean = false): StructureQuery {
return ctx => {
const targetSel = selection(ctx);
if (StructureSelection.structureCount(targetSel) === 0) return targetSel;
const target = inComplement
? structureSubtract(ctx.inputStructure, StructureSelection.unionStructure(targetSel))
: StructureSelection.unionStructure(targetSel);
if (target.elementCount === 0) return StructureSelection.Empty(ctx.inputStructure);
ctx.throwIfTimedOut();
ctx.pushInputStructure(target);
const result = query(ctx);
ctx.popInputStructure();
return StructureSelection.withInputStructure(result, ctx.inputStructure);
}
}