UNPKG

molstar

Version:

A comprehensive macromolecular library.

177 lines 8.29 kB
/** * 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