diff --git a/src/mol-model/structure/query/queries/modifiers.ts b/src/mol-model/structure/query/queries/modifiers.ts index 155aa219ed00487f0b174e18f2d166cd81f5ec4b..96f3151b12ae065e5221a6055ba6ae5923aaa665 100644 --- a/src/mol-model/structure/query/queries/modifiers.ts +++ b/src/mol-model/structure/query/queries/modifiers.ts @@ -4,7 +4,7 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Segmentation } from '../../../../mol-data/int'; +import { Segmentation, SortedArray } from '../../../../mol-data/int'; import { Structure, Unit } from '../../structure'; import { StructureQuery } from '../query'; import { StructureSelection } from '../selection'; @@ -304,78 +304,99 @@ export interface IncludeConnectedParams { } export function includeConnected({ query, layerCount, wholeResidues, bondTest }: IncludeConnectedParams): StructureQuery { + const bt = bondTest || defaultBondTest; + const lc = Math.max(layerCount, 0); return ctx => { - return 0 as any; + const builder = StructureSelection.UniqueBuilder(ctx.inputStructure); + const src = query(ctx); + ctx.pushCurrentLink(); + StructureSelection.forEach(src, (s, sI) => { + let incl = s; + for (let i = 0; i < lc; i++) { + incl = includeConnectedStep(ctx, bt, wholeResidues, incl); + } + builder.add(incl); + if (sI % 10 === 0) ctx.throwIfTimedOut(); + }); + ctx.popCurrentLink(); + return builder.getSelection(); } } -// 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; -// } +function includeConnectedStep(ctx: QueryContext, bondTest: QueryFn<boolean>, wholeResidues: boolean, structure: Structure) { + const expanded = expandConnected(ctx, structure, bondTest); + if (wholeResidues) return getWholeResidues(ctx, ctx.inputStructure, expanded); + return expanded; +} + +function expandConnected(ctx: QueryContext, structure: Structure, bondTest: QueryFn<boolean>) { + const inputStructure = ctx.inputStructure; + const interLinks = inputStructure.links; + const builder = new StructureUniqueSubsetBuilder(inputStructure); + + const processedUnits = new Set<number>(); + + // Process intra unit links + for (const unit of structure.units) { + processedUnits.add(unit.id); + + if (unit.kind !== Unit.Kind.Atomic) { + // add the whole unit + builder.beginUnit(unit.id); + for (let i = 0, _i = unit.elements.length; i < _i; i++) { + builder.addElement(unit.elements[i]); + } + builder.commitUnit(); + continue; + } + + const inputUnit = inputStructure.unitMap.get(unit.id) as Unit.Atomic; + const { offset: intraLinkOffset, b: intraLinkB } = inputUnit.links; + + // Process intra unit links + ctx.atomicLink.aUnit = inputUnit; + ctx.atomicLink.bUnit = inputUnit; + for (let i = 0, _i = unit.elements.length; i < _i; i++) { + // add the current element + builder.addToUnit(unit.id, unit.elements[i]); + + const srcIndex = SortedArray.indexOf(inputUnit.elements, unit.elements[i]); + ctx.atomicLink.aIndex = srcIndex as StructureElement.UnitIndex; + + // check intra unit links + for (let lI = intraLinkOffset[srcIndex], _lI = intraLinkOffset[srcIndex + 1]; lI < _lI; lI++) { + ctx.atomicLink.bIndex = intraLinkB[lI] as StructureElement.UnitIndex; + if (bondTest(ctx)) { + builder.addToUnit(unit.id, inputUnit.elements[intraLinkB[lI]]); + } + } + } + + // Process inter unit links + for (const linkedUnit of interLinks.getLinkedUnits(inputUnit)) { + if (processedUnits.has(linkedUnit.unitB.id)) continue; + + ctx.atomicLink.bUnit = linkedUnit.unitB; + for (const aI of linkedUnit.linkedElementIndices) { + // check if the element is in the expanded structure + if (!SortedArray.has(unit.elements, inputUnit.elements[aI])) continue; + + ctx.atomicLink.aIndex = aI; + for (const bond of linkedUnit.getBonds(aI)) { + ctx.atomicLink.bIndex = bond.indexB; + if (bondTest(ctx)) { + builder.addToUnit(linkedUnit.unitB.id, linkedUnit.unitB.elements[bond.indexB]); + } + } + } + } + } + + return builder.getStructure(); +} + +function defaultBondTest(ctx: QueryContext) { + return true; +} // TODO: unionBy (skip this one?), cluster \ No newline at end of file diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts index 082ce387a25625f54a3f3a993e76beb4b7d5a060..9e839455d75f522b383c13363c7195db8ef3fea9 100644 --- a/src/mol-script/runtime/query/table.ts +++ b/src/mol-script/runtime/query/table.ts @@ -231,6 +231,12 @@ const symbols = [ D(MolScript.structureQuery.modifier.union, (ctx, xs) => Queries.modifiers.union(xs[0] as any)(ctx)), D(MolScript.structureQuery.modifier.expandProperty, (ctx, xs) => Queries.modifiers.expandProperty(xs[0] as any, xs['property'])(ctx)), D(MolScript.structureQuery.modifier.exceptBy, (ctx, xs) => Queries.modifiers.exceptBy(xs[0] as any, xs['by'] as any)(ctx)), + D(MolScript.structureQuery.modifier.includeConnected, (ctx, xs) => Queries.modifiers.includeConnected({ + query: xs[0] as any, + bondTest: xs['bond-test'], + wholeResidues: !!(xs['as-whole-residues'] && xs['as-whole-residues'](ctx)), + layerCount: (xs['layer-count'] && xs['layer-count'](ctx)) || 1 + })(ctx)), // ============= COMBINATORS ================