Skip to content
Snippets Groups Projects
Commit c36c6a6d authored by dsehnal's avatar dsehnal
Browse files

support nested Lookup3D queries

- fixes non-covalent interactions bug
parent 7bfc1b0e
No related branches found
No related tags found
No related merge requests found
...@@ -11,6 +11,7 @@ Note that since we don't clearly distinguish between a public and private interf ...@@ -11,6 +11,7 @@ Note that since we don't clearly distinguish between a public and private interf
- Added NOS-bridges query & improved disulfide-bridges query - Added NOS-bridges query & improved disulfide-bridges query
- Fix #178: ``IndexPairBonds`` for non-single residue structures (bug due to atom reordering). - Fix #178: ``IndexPairBonds`` for non-single residue structures (bug due to atom reordering).
- Add volumetric color smoothing for MolecularSurface and GaussianSurface representations (#173) - Add volumetric color smoothing for MolecularSurface and GaussianSurface representations (#173)
- Fix nested 3d grid lookup that caused results being overwritten in non-covalent interactions computation.
## [v2.0.5] - 2021-04-26 ## [v2.0.5] - 2021-04-26
......
...@@ -40,7 +40,7 @@ export namespace Result { ...@@ -40,7 +40,7 @@ export namespace Result {
export interface Lookup3D<T = number> { export interface Lookup3D<T = number> {
// The result is mutated with each call to find. // The result is mutated with each call to find.
find(x: number, y: number, z: number, radius: number): Result<T>, find(x: number, y: number, z: number, radius: number, result?: Result<T>): Result<T>,
check(x: number, y: number, z: number, radius: number): boolean, check(x: number, y: number, z: number, radius: number): boolean,
readonly boundary: { readonly box: Box3D, readonly sphere: Sphere3D } readonly boundary: { readonly box: Box3D, readonly sphere: Sphere3D }
/** transient result */ /** transient result */
......
...@@ -24,19 +24,20 @@ function GridLookup3D<T extends number = number>(data: PositionData, boundary: B ...@@ -24,19 +24,20 @@ function GridLookup3D<T extends number = number>(data: PositionData, boundary: B
export { GridLookup3D }; export { GridLookup3D };
class GridLookup3DImpl<T extends number = number> implements GridLookup3D<T> { class GridLookup3DImpl<T extends number = number> implements GridLookup3D<T> {
private ctx: QueryContext<T>; private ctx: QueryContext;
boundary: Lookup3D['boundary']; boundary: Lookup3D['boundary'];
buckets: GridLookup3D['buckets']; buckets: GridLookup3D['buckets'];
result: Result<T> result: Result<T>
find(x: number, y: number, z: number, radius: number): Result<T> { find(x: number, y: number, z: number, radius: number, result?: Result<T>): Result<T> {
this.ctx.x = x; this.ctx.x = x;
this.ctx.y = y; this.ctx.y = y;
this.ctx.z = z; this.ctx.z = z;
this.ctx.radius = radius; this.ctx.radius = radius;
this.ctx.isCheck = false; this.ctx.isCheck = false;
query(this.ctx); const ret = result ?? this.result;
return this.ctx.result; query(this.ctx, ret);
return ret;
} }
check(x: number, y: number, z: number, radius: number): boolean { check(x: number, y: number, z: number, radius: number): boolean {
...@@ -45,15 +46,15 @@ class GridLookup3DImpl<T extends number = number> implements GridLookup3D<T> { ...@@ -45,15 +46,15 @@ class GridLookup3DImpl<T extends number = number> implements GridLookup3D<T> {
this.ctx.z = z; this.ctx.z = z;
this.ctx.radius = radius; this.ctx.radius = radius;
this.ctx.isCheck = true; this.ctx.isCheck = true;
return query(this.ctx); return query(this.ctx, this.result);
} }
constructor(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 | number) { constructor(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 | number) {
const structure = build(data, boundary, cellSizeOrCount); const structure = build(data, boundary, cellSizeOrCount);
this.ctx = createContext<T>(structure); this.ctx = createContext(structure);
this.boundary = { box: structure.boundingBox, sphere: structure.boundingSphere }; this.boundary = { box: structure.boundingBox, sphere: structure.boundingSphere };
this.buckets = { offset: structure.bucketOffset, count: structure.bucketCounts, array: structure.bucketArray }; this.buckets = { offset: structure.bucketOffset, count: structure.bucketCounts, array: structure.bucketArray };
this.result = this.ctx.result; this.result = Result.create();
} }
} }
...@@ -215,23 +216,22 @@ function build(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 | ...@@ -215,23 +216,22 @@ function build(data: PositionData, boundary: Boundary, cellSizeOrCount?: Vec3 |
return _build(state); return _build(state);
} }
interface QueryContext<T extends number = number> { interface QueryContext {
grid: Grid3D, grid: Grid3D,
x: number, x: number,
y: number, y: number,
z: number, z: number,
radius: number, radius: number,
result: Result<T>,
isCheck: boolean isCheck: boolean
} }
function createContext<T extends number = number>(grid: Grid3D): QueryContext<T> { function createContext(grid: Grid3D): QueryContext {
return { grid, x: 0.1, y: 0.1, z: 0.1, radius: 0.1, result: Result.create(), isCheck: false }; return { grid, x: 0.1, y: 0.1, z: 0.1, radius: 0.1, isCheck: false };
} }
function query<T extends number = number>(ctx: QueryContext<T>): boolean { function query<T extends number = number>(ctx: QueryContext, result: Result<T>): boolean {
const { min, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices, radius }, delta, maxRadius } = ctx.grid; const { min, size: [sX, sY, sZ], bucketOffset, bucketCounts, bucketArray, grid, data: { x: px, y: py, z: pz, indices, radius }, delta, maxRadius } = ctx.grid;
const { radius: inputRadius, isCheck, x, y, z, result } = ctx; const { radius: inputRadius, isCheck, x, y, z } = ctx;
const r = inputRadius + maxRadius; const r = inputRadius + maxRadius;
const rSq = r * r; const rSq = r * r;
......
...@@ -4,16 +4,17 @@ ...@@ -4,16 +4,17 @@
* @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Alexander Rose <alexander.rose@weirdbyte.de>
*/ */
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { Structure, Unit, StructureElement } from '../../../mol-model/structure';
import { Features } from './features';
import { InteractionType, FeatureType } from './common';
import { IntraContactsBuilder, InterContactsBuilder } from './contacts-builder';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { altLoc, connectedTo, typeSymbol } from '../chemistry/util';
import { OrderedSet } from '../../../mol-data/int'; import { OrderedSet } from '../../../mol-data/int';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { Structure, StructureElement, Unit } from '../../../mol-model/structure';
import { VdwRadius } from '../../../mol-model/structure/model/properties/atomic'; import { VdwRadius } from '../../../mol-model/structure/model/properties/atomic';
import { Elements } from '../../../mol-model/structure/model/properties/atomic/types'; import { Elements } from '../../../mol-model/structure/model/properties/atomic/types';
import { StructureLookup3DResultContext } from '../../../mol-model/structure/structure/util/lookup3d';
import { ParamDefinition as PD } from '../../../mol-util/param-definition';
import { altLoc, connectedTo, typeSymbol } from '../chemistry/util';
import { FeatureType, InteractionType } from './common';
import { InterContactsBuilder, IntraContactsBuilder } from './contacts-builder';
import { Features } from './features';
export const ContactsParams = { export const ContactsParams = {
lineOfSightDistFactor: PD.Numeric(1.0, { min: 0, max: 3, step: 0.1 }), lineOfSightDistFactor: PD.Numeric(1.0, { min: 0, max: 3, step: 0.1 }),
...@@ -71,6 +72,9 @@ const tmpVec = Vec3(); ...@@ -71,6 +72,9 @@ const tmpVec = Vec3();
const tmpVecA = Vec3(); const tmpVecA = Vec3();
const tmpVecB = Vec3(); const tmpVecB = Vec3();
// need to use a separate context for structure.lookup3d.find because of nested queries
const lineOfSightLookupCtx = StructureLookup3DResultContext();
function checkLineOfSight(structure: Structure, infoA: Features.Info, infoB: Features.Info, distFactor: number) { function checkLineOfSight(structure: Structure, infoA: Features.Info, infoB: Features.Info, distFactor: number) {
const featureA = infoA.feature; const featureA = infoA.feature;
const featureB = infoB.feature; const featureB = infoB.feature;
...@@ -83,7 +87,7 @@ function checkLineOfSight(structure: Structure, infoA: Features.Info, infoB: Fea ...@@ -83,7 +87,7 @@ function checkLineOfSight(structure: Structure, infoA: Features.Info, infoB: Fea
const distMax = distFactor * MAX_LINE_OF_SIGHT_DISTANCE; const distMax = distFactor * MAX_LINE_OF_SIGHT_DISTANCE;
const { count, indices, units, squaredDistances } = structure.lookup3d.find(tmpVec[0], tmpVec[1], tmpVec[2], distMax); const { count, indices, units, squaredDistances } = structure.lookup3d.find(tmpVec[0], tmpVec[1], tmpVec[2], distMax, lineOfSightLookupCtx);
if (count === 0) return true; if (count === 0) return true;
for (let r = 0; r < count; ++r) { for (let r = 0; r < count; ++r) {
......
...@@ -12,6 +12,7 @@ import { OrderedSet } from '../../../../mol-data/int'; ...@@ -12,6 +12,7 @@ import { OrderedSet } from '../../../../mol-data/int';
import { StructureUniqueSubsetBuilder } from './unique-subset-builder'; import { StructureUniqueSubsetBuilder } from './unique-subset-builder';
import { StructureElement } from '../element'; import { StructureElement } from '../element';
import { Unit } from '../unit'; import { Unit } from '../unit';
import { UnitIndex } from '../element/util';
export interface StructureResult extends Result<StructureElement.UnitIndex> { export interface StructureResult extends Result<StructureElement.UnitIndex> {
units: Unit[] units: Unit[]
...@@ -40,6 +41,16 @@ export namespace StructureResult { ...@@ -40,6 +41,16 @@ export namespace StructureResult {
} }
} }
export interface StructureLookup3DResultContext {
result: StructureResult,
closeUnitsResult: Result<number>,
unitGroupResult: Result<UnitIndex>,
}
export function StructureLookup3DResultContext(): StructureLookup3DResultContext {
return { result: StructureResult.create(), closeUnitsResult: Result.create(), unitGroupResult: Result.create() };
}
export class StructureLookup3D { export class StructureLookup3D {
private unitLookup: Lookup3D; private unitLookup: Lookup3D;
private pivot = Vec3(); private pivot = Vec3();
...@@ -48,12 +59,17 @@ export class StructureLookup3D { ...@@ -48,12 +59,17 @@ export class StructureLookup3D {
return this.unitLookup.find(x, y, z, radius); return this.unitLookup.find(x, y, z, radius);
} }
private result = StructureResult.create(); private findContext = StructureLookup3DResultContext();
find(x: number, y: number, z: number, radius: number): StructureResult {
Result.reset(this.result); find(x: number, y: number, z: number, radius: number, ctx?: StructureLookup3DResultContext): StructureResult {
return this._find(x, y, z, radius, ctx ?? this.findContext);
}
private _find(x: number, y: number, z: number, radius: number, ctx: StructureLookup3DResultContext): StructureResult {
Result.reset(ctx.result);
const { units } = this.structure; const { units } = this.structure;
const closeUnits = this.unitLookup.find(x, y, z, radius); const closeUnits = this.unitLookup.find(x, y, z, radius, ctx.closeUnitsResult);
if (closeUnits.count === 0) return this.result; if (closeUnits.count === 0) return ctx.result;
for (let t = 0, _t = closeUnits.count; t < _t; t++) { for (let t = 0, _t = closeUnits.count; t < _t; t++) {
const unit = units[closeUnits.indices[t]]; const unit = units[closeUnits.indices[t]];
...@@ -62,12 +78,12 @@ export class StructureLookup3D { ...@@ -62,12 +78,12 @@ export class StructureLookup3D {
Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse); Vec3.transformMat4(this.pivot, this.pivot, unit.conformation.operator.inverse);
} }
const unitLookup = unit.lookup3d; const unitLookup = unit.lookup3d;
const groupResult = unitLookup.find(this.pivot[0], this.pivot[1], this.pivot[2], radius); const groupResult = unitLookup.find(this.pivot[0], this.pivot[1], this.pivot[2], radius, ctx.unitGroupResult);
for (let j = 0, _j = groupResult.count; j < _j; j++) { for (let j = 0, _j = groupResult.count; j < _j; j++) {
StructureResult.add(this.result, unit, groupResult.indices[j], groupResult.squaredDistances[j]); StructureResult.add(ctx.result, unit, groupResult.indices[j], groupResult.squaredDistances[j]);
} }
} }
return this.result; return ctx.result;
} }
findIntoBuilder(x: number, y: number, z: number, radius: number, builder: StructureUniqueSubsetBuilder) { findIntoBuilder(x: number, y: number, z: number, radius: number, builder: StructureUniqueSubsetBuilder) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment