|
@@ -14,6 +14,7 @@ import { QueryContext, QueryFn } from '../context';
|
|
|
import { structureIntersect, structureSubtract } from '../utils/structure-set';
|
|
|
import { UniqueArray } from 'mol-data/generic';
|
|
|
import { StructureSubsetBuilder } from '../../structure/util/subset-builder';
|
|
|
+import StructureElement from '../../structure/element';
|
|
|
|
|
|
function getWholeResidues(ctx: QueryContext, source: Structure, structure: Structure) {
|
|
|
const builder = source.subsetBuilder(true);
|
|
@@ -59,8 +60,7 @@ export function wholeResidues(query: StructureQuery): StructureQuery {
|
|
|
|
|
|
export interface IncludeSurroundingsParams {
|
|
|
radius: number,
|
|
|
- // TODO
|
|
|
- // atomRadius?: Element.Property<number>,
|
|
|
+ elementRadius?: QueryFn<number>,
|
|
|
wholeResidues?: boolean
|
|
|
}
|
|
|
|
|
@@ -82,9 +82,88 @@ function getIncludeSurroundings(ctx: QueryContext, source: Structure, structure:
|
|
|
return !!params.wholeResidues ? getWholeResidues(ctx, source, builder.getStructure()) : builder.getStructure();
|
|
|
}
|
|
|
|
|
|
+interface IncludeSurroundingsParamsWithRadius extends IncludeSurroundingsParams {
|
|
|
+ elementRadius: QueryFn<number>,
|
|
|
+ elementRadiusClosure: StructureElement.Property<number>,
|
|
|
+ sourceMaxRadius: number
|
|
|
+}
|
|
|
+
|
|
|
+function getIncludeSurroundingsWithRadius(ctx: QueryContext, source: Structure, structure: Structure, params: IncludeSurroundingsParamsWithRadius) {
|
|
|
+ const builder = new StructureUniqueSubsetBuilder(source);
|
|
|
+ const lookup = source.lookup3d;
|
|
|
+ const { elementRadius, elementRadiusClosure, sourceMaxRadius, radius } = params;
|
|
|
+
|
|
|
+ ctx.pushCurrentElement();
|
|
|
+ for (const unit of structure.units) {
|
|
|
+ ctx.element.unit = unit;
|
|
|
+ const { x, y, z } = unit.conformation;
|
|
|
+ const elements = unit.elements;
|
|
|
+
|
|
|
+ for (let i = 0, _i = elements.length; i < _i; i++) {
|
|
|
+ const e = elements[i];
|
|
|
+ ctx.element.element = e;
|
|
|
+ const eRadius = elementRadius(ctx);
|
|
|
+ lookup.findIntoBuilderWithRadius(x(e), y(e), z(e), eRadius, sourceMaxRadius, radius, elementRadiusClosure, builder);
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx.throwIfTimedOut();
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx.popCurrentElement();
|
|
|
+ return !!params.wholeResidues ? getWholeResidues(ctx, source, builder.getStructure()) : builder.getStructure();
|
|
|
+}
|
|
|
+
|
|
|
+function createElementRadiusFn(ctx: QueryContext, eRadius: QueryFn<number>): StructureElement.Property<number> {
|
|
|
+ return e => {
|
|
|
+ ctx.element.unit = e.unit;
|
|
|
+ ctx.element.element = e.element;
|
|
|
+ return eRadius(ctx);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function findStructureRadius(ctx: QueryContext, eRadius: QueryFn<number>) {
|
|
|
+ let r = 0;
|
|
|
+ for (const unit of ctx.inputStructure.units) {
|
|
|
+ ctx.element.unit = unit;
|
|
|
+ const elements = unit.elements;
|
|
|
+
|
|
|
+ for (let i = 0, _i = elements.length; i < _i; i++) {
|
|
|
+ const e = elements[i];
|
|
|
+ ctx.element.element = e;
|
|
|
+ const eR = eRadius(ctx);
|
|
|
+ if (eR > r) r = eR;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ ctx.throwIfTimedOut();
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
export function includeSurroundings(query: StructureQuery, params: IncludeSurroundingsParams): StructureQuery {
|
|
|
return ctx => {
|
|
|
const inner = query(ctx);
|
|
|
+
|
|
|
+ if (params.elementRadius) {
|
|
|
+ const prms: IncludeSurroundingsParamsWithRadius = {
|
|
|
+ ...params,
|
|
|
+ elementRadius: params.elementRadius,
|
|
|
+ elementRadiusClosure: createElementRadiusFn(ctx, params.elementRadius),
|
|
|
+ sourceMaxRadius: findStructureRadius(ctx, params.elementRadius)
|
|
|
+ };
|
|
|
+
|
|
|
+ if (StructureSelection.isSingleton(inner)) {
|
|
|
+ const surr = getIncludeSurroundingsWithRadius(ctx, ctx.inputStructure, inner.structure, prms);
|
|
|
+ const ret = StructureSelection.Singletons(ctx.inputStructure, surr);
|
|
|
+ return ret;
|
|
|
+ } else {
|
|
|
+ const builder = new UniqueStructuresBuilder(ctx.inputStructure);
|
|
|
+ for (const s of inner.structures) {
|
|
|
+ builder.add(getIncludeSurroundingsWithRadius(ctx, ctx.inputStructure, s, prms));
|
|
|
+ }
|
|
|
+ return builder.getSelection();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (StructureSelection.isSingleton(inner)) {
|
|
|
const surr = getIncludeSurroundings(ctx, ctx.inputStructure, inner.structure, params);
|
|
|
const ret = StructureSelection.Singletons(ctx.inputStructure, surr);
|
|
@@ -218,4 +297,86 @@ export function expandProperty(query: StructureQuery, property: QueryFn): Struct
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-// TODO: unionBy (skip this one?), cluster, includeConnected, includeSurroundings with "radii"
|
|
|
+export interface IncludeConnectedParams {
|
|
|
+ query: StructureQuery,
|
|
|
+ bondTest?: QueryFn<boolean>,
|
|
|
+ layerCount: number,
|
|
|
+ wholeResidues: boolean
|
|
|
+}
|
|
|
+
|
|
|
+export function includeConnected({ query, layerCount, wholeResidues, bondTest }: IncludeConnectedParams): StructureQuery {
|
|
|
+ return ctx => {
|
|
|
+ return 0 as any;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// function defaultBondTest(ctx: QueryContext) {
|
|
|
+// return true;
|
|
|
+// }
|
|
|
+
|
|
|
+// interface IncludeConnectedCtx {
|
|
|
+// queryCtx: QueryContext,
|
|
|
+// input: Structure,
|
|
|
+// bondTest: QueryFn<boolean>,
|
|
|
+// wholeResidues: boolean
|
|
|
+// }
|
|
|
+
|
|
|
+// type FrontierSet = UniqueArray<StructureElement.UnitIndex, StructureElement.UnitIndex>
|
|
|
+// type Frontier = { unitIds: UniqueArray<number>, elements: Map<number /* unit id */, FrontierSet> }
|
|
|
+
|
|
|
+// namespace Frontier {
|
|
|
+// export function has({ elements }: Frontier, unitId: number, element: StructureElement.UnitIndex) {
|
|
|
+// if (!elements.has(unitId)) return false;
|
|
|
+// const xs = elements.get(unitId)!;
|
|
|
+// return xs.keys.has(element);
|
|
|
+// }
|
|
|
+
|
|
|
+// export function create(pivot: Structure, input: Structure) {
|
|
|
+// const unitIds = UniqueArray.create<number>();
|
|
|
+// const elements: Frontier['elements'] = new Map();
|
|
|
+// for (const unit of pivot.units) {
|
|
|
+// if (!Unit.isAtomic(unit)) continue;
|
|
|
+
|
|
|
+// UniqueArray.add(unitIds, unit.id, unit.id);
|
|
|
+// const xs: FrontierSet = UniqueArray.create();
|
|
|
+// elements.set(unit.id, xs);
|
|
|
+
|
|
|
+// const pivotElements = unit.elements;
|
|
|
+// const inputElements = input.unitMap.get(unit.id).elements;
|
|
|
+// for (let i = 0, _i = pivotElements.length; i < _i; i++) {
|
|
|
+// const idx = SortedArray.indexOf(inputElements, pivotElements[i]) as StructureElement.UnitIndex;
|
|
|
+// UniqueArray.add(xs, idx, idx);
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// return { unitIds, elements };
|
|
|
+// }
|
|
|
+
|
|
|
+// export function addFrontier(target: Frontier, from: Frontier) {
|
|
|
+// for (const unitId of from.unitIds.array) {
|
|
|
+// let xs: FrontierSet;
|
|
|
+// if (target.elements.has(unitId)) {
|
|
|
+// xs = target.elements.get(unitId)!;
|
|
|
+// } else {
|
|
|
+// xs = UniqueArray.create();
|
|
|
+// target.elements.set(unitId, xs);
|
|
|
+// UniqueArray.add(target.unitIds, unitId, unitId);
|
|
|
+// }
|
|
|
+
|
|
|
+// for (const e of from.elements.get(unitId)!.array) {
|
|
|
+// UniqueArray.add(xs, e, e);
|
|
|
+// }
|
|
|
+// }
|
|
|
+// return target;
|
|
|
+// }
|
|
|
+
|
|
|
+// export function includeWholeResidues(structure: Structure, frontier: Frontier) {
|
|
|
+// // ...
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// function expandFrontier(ctx: IncludeConnectedCtx, currentFrontier: Frontier, result: Frontier): Frontier {
|
|
|
+// return 0 as any;
|
|
|
+// }
|
|
|
+
|
|
|
+// TODO: unionBy (skip this one?), cluster
|