Browse Source

mol-model Structure distance checking

David Sehnal 6 years ago
parent
commit
51395c1a13

+ 7 - 1
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();

+ 13 - 29
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;
         }

+ 116 - 0
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;
+    }
+}