molstar
Version:
A comprehensive macromolecular library.
177 lines • 8.29 kB
JavaScript
/**
* Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
import { __assign } from "tslib";
import { OrderedSet } from '../../../mol-data/int';
import { Mat4, Vec3 } from '../../../mol-math/linear-algebra';
import { Unit } from '../../../mol-model/structure';
import { VdwRadius } from '../../../mol-model/structure/model/properties/atomic';
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 { Features } from './features';
export var ContactsParams = {
lineOfSightDistFactor: PD.Numeric(1.0, { min: 0, max: 3, step: 0.1 }),
};
var MAX_LINE_OF_SIGHT_DISTANCE = 3;
function validPair(structure, infoA, infoB) {
var indexA = infoA.members[infoA.offsets[infoA.feature]];
var indexB = infoB.members[infoB.offsets[infoB.feature]];
if (indexA === indexB && infoA.unit === infoB.unit)
return false; // no self interaction
var altA = altLoc(infoA.unit, indexA);
var altB = altLoc(infoB.unit, indexB);
if (altA && altB && altA !== altB)
return false; // incompatible alternate location id
if (infoA.unit.residueIndex[infoA.unit.elements[indexA]] === infoB.unit.residueIndex[infoB.unit.elements[indexB]] && infoA.unit === infoB.unit)
return false; // same residue
// e.g. no hbond if donor and acceptor are bonded
if (connectedTo(structure, infoA.unit, indexA, infoB.unit, indexB))
return false;
return true;
}
//
function invalidAltLoc(unitA, indexA, unitB, indexB) {
var altA = altLoc(unitA, indexA);
var altB = altLoc(unitB, indexB);
return altA && altB && altA !== altB;
}
function isMember(element, info) {
var feature = info.feature, offsets = info.offsets, members = info.members;
for (var i = offsets[feature], il = offsets[feature + 1]; i < il; ++i) {
if (members[i] === element)
return true;
}
return false;
}
var tmpVec = Vec3();
var tmpVecA = Vec3();
var tmpVecB = Vec3();
// need to use a separate context for structure.lookup3d.find because of nested queries
var lineOfSightLookupCtx = StructureLookup3DResultContext();
function checkLineOfSight(structure, infoA, infoB, distFactor) {
var featureA = infoA.feature;
var featureB = infoB.feature;
var indexA = infoA.members[infoA.offsets[featureA]];
var indexB = infoB.members[infoB.offsets[featureB]];
Features.position(tmpVecA, infoA);
Features.position(tmpVecB, infoB);
Vec3.scale(tmpVec, Vec3.add(tmpVec, tmpVecA, tmpVecB), 0.5);
var distMax = distFactor * MAX_LINE_OF_SIGHT_DISTANCE;
var _a = structure.lookup3d.find(tmpVec[0], tmpVec[1], tmpVec[2], distMax, lineOfSightLookupCtx), count = _a.count, indices = _a.indices, units = _a.units, squaredDistances = _a.squaredDistances;
if (count === 0)
return true;
for (var r = 0; r < count; ++r) {
var i = indices[r];
var unit = units[r];
if (!Unit.isAtomic(unit))
continue;
var element = typeSymbol(unit, i);
// allow hydrogens
if (element === "H" /* H */)
continue;
var vdw = VdwRadius(element);
// check distance
if (vdw * vdw * distFactor * distFactor <= squaredDistances[r])
continue;
// allow different altlocs
if (invalidAltLoc(unit, i, infoA.unit, indexA) || invalidAltLoc(unit, i, infoB.unit, indexB))
continue;
// allow member atoms
if ((infoA.unit === unit && isMember(i, infoA)) || (infoB.unit === unit && isMember(i, infoB)))
continue;
unit.conformation.position(unit.elements[i], tmpVec);
// allow atoms at the center of functional groups
if (Vec3.squaredDistance(tmpVec, tmpVecA) < 1 || Vec3.squaredDistance(tmpVec, tmpVecB) < 1)
continue;
return false;
}
return true;
}
/**
* Add all intra-unit contacts, i.e. pairs of features
*/
export function addUnitContacts(structure, unit, features, builder, testers, props) {
for (var _i = 0, testers_1 = testers; _i < testers_1.length; _i++) {
var tester = testers_1[_i];
_addUnitContacts(structure, unit, features, builder, tester, props);
}
}
function _addUnitContacts(structure, unit, features, builder, tester, props) {
var x = features.x, y = features.y, z = features.z;
var _a = features.subset(tester.requiredFeatures), lookup3d = _a.lookup3d, subsetIndices = _a.indices;
var infoA = Features.Info(structure, unit, features);
var infoB = __assign({}, infoA);
var distFactor = props.lineOfSightDistFactor;
for (var t = 0, tl = OrderedSet.size(subsetIndices); t < tl; ++t) {
var i = OrderedSet.getAt(subsetIndices, t);
var _b = lookup3d.find(x[i], y[i], z[i], tester.maxDistance), count = _b.count, indices = _b.indices, squaredDistances = _b.squaredDistances;
if (count === 0)
continue;
infoA.feature = i;
for (var r = 0; r < count; ++r) {
var j = OrderedSet.getAt(subsetIndices, indices[r]);
if (j <= i)
continue;
infoB.feature = j;
if (!validPair(structure, infoA, infoB))
continue;
var type = tester.getType(structure, infoA, infoB, squaredDistances[r]);
if (type && checkLineOfSight(structure, infoA, infoB, distFactor)) {
builder.add(i, j, type);
}
}
}
}
var _imageTransform = Mat4();
/**
* Add all inter-unit contacts, i.e. pairs of features
*/
export function addStructureContacts(structure, unitA, featuresA, unitB, featuresB, builder, testers, props) {
var countA = featuresA.count, xA = featuresA.x, yA = featuresA.y, zA = featuresA.z;
var lookup3d = featuresB.lookup3d;
// the lookup queries need to happen in the "unitB space".
// that means imageA = inverseOperB(operA(i))
var imageTransform = Mat4.mul(_imageTransform, unitB.conformation.operator.inverse, unitA.conformation.operator.matrix);
var isNotIdentity = !Mat4.isIdentity(imageTransform);
var imageA = Vec3();
var maxDistance = Math.max.apply(Math, testers.map(function (t) { return t.maxDistance; }));
var _a = lookup3d.boundary.sphere, center = _a.center, radius = _a.radius;
var testDistanceSq = (radius + maxDistance) * (radius + maxDistance);
var distFactor = props.lineOfSightDistFactor;
var infoA = Features.Info(structure, unitA, featuresA);
var infoB = Features.Info(structure, unitB, featuresB);
builder.startUnitPair(unitA, unitB);
for (var i = 0; i < countA; ++i) {
Vec3.set(imageA, xA[i], yA[i], zA[i]);
if (isNotIdentity)
Vec3.transformMat4(imageA, imageA, imageTransform);
if (Vec3.squaredDistance(imageA, center) > testDistanceSq)
continue;
var _b = lookup3d.find(imageA[0], imageA[1], imageA[2], maxDistance), indices = _b.indices, count = _b.count, squaredDistances = _b.squaredDistances;
if (count === 0)
continue;
infoA.feature = i;
for (var r = 0; r < count; ++r) {
var j = indices[r];
infoB.feature = j;
if (!validPair(structure, infoA, infoB))
continue;
var distanceSq = squaredDistances[r];
for (var _i = 0, testers_2 = testers; _i < testers_2.length; _i++) {
var tester = testers_2[_i];
if (distanceSq < tester.maxDistance * tester.maxDistance) {
var type = tester.getType(structure, infoA, infoB, distanceSq);
if (type && checkLineOfSight(structure, infoA, infoB, distFactor)) {
builder.add(i, j, type);
break;
}
}
}
}
}
builder.finishUnitPair();
}
//# sourceMappingURL=contacts.js.map