diff --git a/src/mol-model/structure/query/context.ts b/src/mol-model/structure/query/context.ts index 772fae563744ef4243990e06bfa840aaafae1674..245cd5b37d400a1f3272669ada830123f6423152 100644 --- a/src/mol-model/structure/query/context.ts +++ b/src/mol-model/structure/query/context.ts @@ -4,8 +4,9 @@ * @author David Sehnal <david.sehnal@gmail.com> */ -import { Structure, StructureElement } from '../structure'; +import { Structure, StructureElement, Unit } from '../structure'; import { now } from 'mol-task'; +import { ElementIndex } from '../model'; export interface QueryContextView { readonly element: StructureElement; @@ -26,6 +27,11 @@ export class QueryContext implements QueryContextView { readonly element: StructureElement = StructureElement.create(); currentStructure: Structure = void 0 as any; + setElement(unit: Unit, e: ElementIndex) { + this.element.unit = unit; + this.element.element = e; + } + pushCurrentElement(): StructureElement { this.currentElementStack[this.currentElementStack.length] = this.element; (this.element as StructureElement) = StructureElement.create(); diff --git a/src/mol-model/structure/query/queries/filters.ts b/src/mol-model/structure/query/queries/filters.ts index 06f8ce8a4a84e65a3b68bb40841af3066f72589e..699a774509424b044aa0b3aa18350e8feff172db 100644 --- a/src/mol-model/structure/query/queries/filters.ts +++ b/src/mol-model/structure/query/queries/filters.ts @@ -107,7 +107,7 @@ export interface WithinParams { target: StructureQuery, minRadius?: number, maxRadius: number, - atomRadius?: QueryFn<number>, + elementRadius?: QueryFn<number>, invert?: boolean } @@ -120,12 +120,12 @@ export function within(params: WithinParams): StructureQuery { selection: params.query(queryCtx), target: params.target(queryCtx), maxRadius: params.maxRadius, - minRadius: params.minRadius ? params.minRadius : 0, - atomRadius: params.atomRadius || _zeroRadius, + minRadius: params.minRadius ? Math.max(0, params.minRadius) : 0, + elementRadius: params.elementRadius!, invert: !!params.invert, } - if (ctx.minRadius === 0 && ctx.atomRadius === _zeroRadius) { + if (ctx.minRadius === 0 && typeof params.minRadius === 'undefined') { return withinMaxRadius(ctx); } else { // TODO @@ -142,7 +142,7 @@ interface WithinContext { minRadius: number, maxRadius: number, invert: boolean, - atomRadius: QueryFn<number> + elementRadius: QueryFn<number> } function withinMaxRadius({ queryCtx, selection, target, maxRadius, invert }: WithinContext) { const targetLookup = StructureSelection.unionStructure(target).lookup3d; @@ -151,34 +151,18 @@ function withinMaxRadius({ queryCtx, selection, target, maxRadius, invert }: Wit const pos = Vec3.zero(); StructureSelection.forEach(selection, (s, sI) => { const { units } = s; - let withinRadius = false; for (let i = 0, _i = units.length; i < _i; i++) { const unit = units[i]; - const { elements, conformation } = unit; - - switch (unit.kind) { - case Unit.Kind.Atomic: - // TODO: assign radius to gaussian elements? - case Unit.Kind.Gaussians: - for (let i = 0, _i = elements.length; i < _i; i++) { - conformation.position(elements[i], pos); - if (targetLookup.check(pos[0], pos[1], pos[2], maxRadius)) { - withinRadius = true; - break; - } - } - break; - case Unit.Kind.Spheres: - const radius = unit.coarseConformation.radius; - for (let i = 0, _i = elements.length; i < _i; i++) { - conformation.position(elements[i], pos); - if (targetLookup.check(pos[0], pos[1], pos[2], maxRadius + radius[elements[i]])) { - withinRadius = true; - break; - } - } + const { elements, conformation: { position, r } } = unit; + + for (let i = 0, _i = elements.length; i < _i; i++) { + const e = elements[i]; + position(e, pos); + if (targetLookup.check(pos[0], pos[1], pos[2], maxRadius + r(e))) { + withinRadius = true; break; + } } if (withinRadius) break; } diff --git a/src/mol-model/structure/query/utils/structure-distance.ts b/src/mol-model/structure/query/utils/structure-distance.ts new file mode 100644 index 0000000000000000000000000000000000000000..315292b751137ce779e2b349e61cbdaf15adffa8 --- /dev/null +++ b/src/mol-model/structure/query/utils/structure-distance.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 { Structure, Unit } from '../../structure' +import { Vec3 } from 'mol-math/linear-algebra'; +import { QueryFn, QueryContext } from '../context'; + +export function checkStructureMinMaxDistance(ctx: QueryContext, a: Structure, b: Structure, minDist: number, maxDist: number, elementRadius: QueryFn<number>) { + if (a.elementCount === 0 || b.elementCount === 0) return true; + + if (a.elementCount <= b.elementCount) return MinMaxDist.check(ctx, a, b, minDist, maxDist, elementRadius); + return MinMaxDist.check(ctx, b, a, minDist, maxDist, elementRadius); +} + +export function checkStructureMaxRadiusDistance(ctx: QueryContext, a: Structure, b: Structure, maxDist: number, elementRadius: QueryFn<number>) { + if (a.elementCount === 0 || b.elementCount === 0) return true; + + if (a.elementCount <= b.elementCount) return MaxRadiusDist.check(ctx, a, b, maxDist, elementRadius); + return MaxRadiusDist.check(ctx, b, a, maxDist, elementRadius); +} + +namespace MinMaxDist { + const enum Result { + BelowMin, + WithinMax, + Miss + } + + const distVec = Vec3.zero(); + function inUnit(ctx: QueryContext, unit: Unit, p: Vec3, eRadius: number, minDist: number, maxDist: number, elementRadius: QueryFn<number>) { + const { elements, conformation: { position } } = unit, dV = distVec; + ctx.element.unit = unit; + let withinRange = false; + for (let i = 0, _i = elements.length; i < _i; i++) { + const e = elements[i]; + ctx.element.element = e; + const d = Math.max(0, Vec3.distance(p, position(e, dV)) - eRadius - elementRadius(ctx)); + if (d < minDist) return Result.BelowMin; + if (d < maxDist) withinRange = true; + } + return withinRange ? Result.WithinMax : Result.Miss; + } + + export function toPoint(ctx: QueryContext, s: Structure, point: Vec3, radius: number, minDist: number, maxDist: number, elementRadius: QueryFn<number>) { + const { units } = s; + let withinRange = false; + for (let i = 0, _i = units.length; i < _i; i++) { + const iu = inUnit(ctx, units[i], point, radius, minDist, maxDist, elementRadius); + if (iu === Result.BelowMin) return Result.BelowMin; + if (iu === Result.WithinMax) withinRange = true; + } + return withinRange ? Result.WithinMax : Result.Miss; + } + + const distPivot = Vec3.zero(); + export function check(ctx: QueryContext, a: Structure, b: Structure, minDist: number, maxDist: number, elementRadius: QueryFn<number>) { + if (a.elementCount === 0 || b.elementCount === 0) return 0; + + const { units } = a; + let withinRange = false; + for (let i = 0, _i = units.length; i < _i; i++) { + const unit = units[i]; + const { elements, conformation: { position } } = unit; + for (let i = 0, _i = elements.length; i < _i; i++) { + const e = elements[i]; + ctx.setElement(unit, e); + const tp = toPoint(ctx, b, position(e, distPivot), elementRadius(ctx), minDist, maxDist, elementRadius); + if (tp === Result.BelowMin) return Result.BelowMin; + if (tp === Result.WithinMax) withinRange = true; + } + } + return withinRange; + } +} + +namespace MaxRadiusDist { + const distVec = Vec3.zero(); + function inUnit(ctx: QueryContext, unit: Unit, p: Vec3, eRadius: number, maxDist: number, elementRadius: QueryFn<number>) { + const { elements, conformation: { position } } = unit, dV = distVec; + ctx.element.unit = unit; + for (let i = 0, _i = elements.length; i < _i; i++) { + const e = elements[i]; + ctx.element.element = e; + if (Math.max(0, Vec3.distance(p, position(e, dV)) - eRadius - elementRadius(ctx)) <= maxDist) return true; + } + return false; + } + + export function toPoint(ctx: QueryContext, s: Structure, point: Vec3, radius: number, maxDist: number, elementRadius: QueryFn<number>) { + const { units } = s; + for (let i = 0, _i = units.length; i < _i; i++) { + if (inUnit(ctx, units[i], point, radius, maxDist, elementRadius)) return true; + } + return false; + } + + const distPivot = Vec3.zero(); + export function check(ctx: QueryContext, a: Structure, b: Structure, maxDist: number, elementRadius: QueryFn<number>) { + if (a.elementCount === 0 || b.elementCount === 0) return 0; + + const { units } = a; + for (let i = 0, _i = units.length; i < _i; i++) { + const unit = units[i]; + const { elements, conformation: { position } } = unit; + for (let i = 0, _i = elements.length; i < _i; i++) { + const e = elements[i]; + ctx.setElement(unit, e); + if (toPoint(ctx, b, position(e, distPivot), elementRadius(ctx), maxDist, elementRadius)) return true; + } + } + return false; + } +} \ No newline at end of file