UNPKG

molstar

Version:

A comprehensive macromolecular library.

326 lines 15.7 kB
"use strict"; /** * Copyright (c) 2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Fred Ludlow <Fred.Ludlow@astx.com> * * based in part on NGL (https://github.com/arose/ngl) */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CationPiProvider = exports.PiStackingProvider = exports.IonicProvider = exports.AromaticRingProvider = exports.PositiveChargeProvider = exports.NegativChargeProvider = void 0; var param_definition_1 = require("../../../mol-util/param-definition"); var features_1 = require("./features"); var types_1 = require("../../../mol-model/structure/model/types"); var util_1 = require("../chemistry/util"); var valence_model_1 = require("../valence-model"); var misc_1 = require("../../../mol-math/misc"); var int_1 = require("../../../mol-data/int"); var functional_group_1 = require("../chemistry/functional-group"); var linear_algebra_1 = require("../../../mol-math/linear-algebra"); var IonicParams = { distanceMax: param_definition_1.ParamDefinition.Numeric(5.0, { min: 0, max: 8, step: 0.1 }), }; var PiStackingParams = { distanceMax: param_definition_1.ParamDefinition.Numeric(5.5, { min: 1, max: 8, step: 0.1 }), offsetMax: param_definition_1.ParamDefinition.Numeric(2.0, { min: 0, max: 4, step: 0.1 }), angleDevMax: param_definition_1.ParamDefinition.Numeric(30, { min: 0, max: 180, step: 1 }), }; var CationPiParams = { distanceMax: param_definition_1.ParamDefinition.Numeric(6.0, { min: 1, max: 8, step: 0.1 }), offsetMax: param_definition_1.ParamDefinition.Numeric(2.0, { min: 0, max: 4, step: 0.1 }), }; // var PositvelyCharged = ['ARG', 'HIS', 'LYS']; var NegativelyCharged = ['GLU', 'ASP']; function getUnitValenceModel(structure, unit) { var valenceModel = valence_model_1.ValenceModelProvider.get(structure).value; if (!valenceModel) throw Error('expected valence model to be available'); var unitValenceModel = valenceModel.get(unit.id); if (!unitValenceModel) throw Error('expected valence model for unit to be available'); return unitValenceModel; } function addUnitPositiveCharges(structure, unit, builder) { var charge = getUnitValenceModel(structure, unit).charge; var elements = unit.elements; var _a = unit.model.atomicConformation, x = _a.x, y = _a.y, z = _a.z; var addedElements = new Set(); var label_comp_id = unit.model.atomicHierarchy.atoms.label_comp_id; var residueIt = int_1.Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements); while (residueIt.hasNext) { var _b = residueIt.move(), residueIndex = _b.index, start = _b.start, end = _b.end; var compId = label_comp_id.value(unit.model.atomicHierarchy.residueAtomSegments.offsets[residueIndex]); if (PositvelyCharged.includes(compId)) { builder.startState(); for (var j = start; j < end; ++j) { if ((0, util_1.typeSymbol)(unit, j) === "N" /* N */ && !types_1.ProteinBackboneAtoms.has((0, util_1.atomId)(unit, j))) { builder.pushMember(x[elements[j]], y[elements[j]], z[elements[j]], j); } } builder.finishState(1 /* PositiveCharge */, 0 /* None */); } else if (!types_1.PolymerNames.has(compId)) { addedElements.clear(); for (var j = start; j < end; ++j) { var group = 0 /* None */; if ((0, functional_group_1.isGuanidine)(structure, unit, j)) { group = 8 /* Guanidine */; } else if ((0, functional_group_1.isAcetamidine)(structure, unit, j)) { group = 9 /* Acetamidine */; } if (group) { builder.startState(); (0, util_1.eachBondedAtom)(structure, unit, j, function (_, k) { if ((0, util_1.typeSymbol)(unit, k) === "N" /* N */) { addedElements.add(k); builder.pushMember(x[elements[k]], y[elements[k]], z[elements[k]], k); } }); builder.finishState(1 /* PositiveCharge */, group); } } for (var j = start; j < end; ++j) { if (charge[j] > 0 && !addedElements.has(j)) { builder.add(1 /* PositiveCharge */, 0 /* None */, x[elements[j]], y[elements[j]], z[elements[j]], j); } } } } } function addUnitNegativeCharges(structure, unit, builder) { var charge = getUnitValenceModel(structure, unit).charge; var elements = unit.elements; var _a = unit.model.atomicConformation, x = _a.x, y = _a.y, z = _a.z; var addedElements = new Set(); var label_comp_id = unit.model.atomicHierarchy.atoms.label_comp_id; var residueIt = int_1.Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, elements); while (residueIt.hasNext) { var _b = residueIt.move(), residueIndex = _b.index, start = _b.start, end = _b.end; var compId = label_comp_id.value(unit.model.atomicHierarchy.residueAtomSegments.offsets[residueIndex]); if (NegativelyCharged.includes(compId)) { builder.startState(); for (var j = start; j < end; ++j) { if ((0, util_1.typeSymbol)(unit, j) === "O" /* O */ && !types_1.ProteinBackboneAtoms.has((0, util_1.atomId)(unit, j))) { builder.pushMember(x[elements[j]], y[elements[j]], z[elements[j]], j); } } builder.finishState(2 /* NegativeCharge */, 0 /* None */); } else if (types_1.BaseNames.has(compId)) { for (var j = start; j < end; ++j) { if ((0, functional_group_1.isPhosphate)(structure, unit, j)) { builder.startState(); (0, util_1.eachBondedAtom)(structure, unit, j, function (_, k) { if ((0, util_1.typeSymbol)(unit, k) === "O" /* O */) { builder.pushMember(x[elements[k]], y[elements[k]], z[elements[k]], k); } }); builder.finishState(2 /* NegativeCharge */, 6 /* Phosphate */); } } } else if (!types_1.PolymerNames.has(compId)) { for (var j = start; j < end; ++j) { builder.startState(); if ((0, util_1.typeSymbol)(unit, j) === "N" /* N */ && !types_1.ProteinBackboneAtoms.has((0, util_1.atomId)(unit, j))) { builder.pushMember(x[elements[j]], y[elements[j]], z[elements[j]], j); } builder.finishState(2 /* NegativeCharge */, 0 /* None */); var group = 0 /* None */; if ((0, functional_group_1.isSulfonicAcid)(structure, unit, j)) { group = 4 /* SulfonicAcid */; } else if ((0, functional_group_1.isPhosphate)(structure, unit, j)) { group = 6 /* Phosphate */; } else if ((0, functional_group_1.isSulfate)(structure, unit, j)) { group = 5 /* Sulfate */; } else if ((0, functional_group_1.isCarboxylate)(structure, unit, j)) { group = 10 /* Carboxylate */; } if (group) { builder.startState(); (0, util_1.eachBondedAtom)(structure, unit, j, function (_, k) { if ((0, util_1.typeSymbol)(unit, k) === "O" /* O */) { addedElements.add(k); builder.pushMember(x[elements[k]], y[elements[k]], z[elements[k]], k); } }); builder.finishState(2 /* NegativeCharge */, group); } } for (var j = start; j < end; ++j) { if (charge[j] < 0 && !addedElements.has(j)) { builder.add(2 /* NegativeCharge */, 0 /* None */, x[elements[j]], y[elements[j]], z[elements[j]], j); } } } } } function addUnitAromaticRings(structure, unit, builder) { var elements = unit.elements; var _a = unit.model.atomicConformation, x = _a.x, y = _a.y, z = _a.z; for (var _i = 0, _b = unit.rings.aromaticRings; _i < _b.length; _i++) { var ringIndex = _b[_i]; var ring = unit.rings.all[ringIndex]; builder.startState(); for (var i = 0, il = ring.length; i < il; ++i) { var j = ring[i]; builder.pushMember(x[elements[j]], y[elements[j]], z[elements[j]], j); } builder.finishState(3 /* AromaticRing */, 0 /* None */); } } function isIonic(ti, tj) { return ((ti === 2 /* NegativeCharge */ && tj === 1 /* PositiveCharge */) || (ti === 1 /* PositiveCharge */ && tj === 2 /* NegativeCharge */)); } function isPiStacking(ti, tj) { return ti === 3 /* AromaticRing */ && tj === 3 /* AromaticRing */; } function isCationPi(ti, tj) { return ((ti === 3 /* AromaticRing */ && tj === 1 /* PositiveCharge */) || (ti === 1 /* PositiveCharge */ && tj === 3 /* AromaticRing */)); } var tmpPointA = (0, linear_algebra_1.Vec3)(); var tmpPointB = (0, linear_algebra_1.Vec3)(); function areFeaturesWithinDistanceSq(infoA, infoB, distanceSq) { var featureA = infoA.feature, offsetsA = infoA.offsets, membersA = infoA.members; var featureB = infoB.feature, offsetsB = infoB.offsets, membersB = infoB.members; for (var i = offsetsA[featureA], il = offsetsA[featureA + 1]; i < il; ++i) { var elementA = membersA[i]; infoA.unit.conformation.position(infoA.unit.elements[elementA], tmpPointA); for (var j = offsetsB[featureB], jl = offsetsB[featureB + 1]; j < jl; ++j) { var elementB = membersB[j]; infoB.unit.conformation.position(infoB.unit.elements[elementB], tmpPointB); if (linear_algebra_1.Vec3.squaredDistance(tmpPointA, tmpPointB) < distanceSq) return true; } } return false; } var tmpVecA = (0, linear_algebra_1.Vec3)(); var tmpVecB = (0, linear_algebra_1.Vec3)(); var tmpVecC = (0, linear_algebra_1.Vec3)(); var tmpVecD = (0, linear_algebra_1.Vec3)(); function getNormal(out, info) { var unit = info.unit, feature = info.feature, offsets = info.offsets, members = info.members; var elements = unit.elements; var i = offsets[feature]; info.unit.conformation.position(elements[members[i]], tmpVecA); info.unit.conformation.position(elements[members[i + 1]], tmpVecB); info.unit.conformation.position(elements[members[i + 2]], tmpVecC); return linear_algebra_1.Vec3.triangleNormal(out, tmpVecA, tmpVecB, tmpVecC); } var getOffset = function (infoA, infoB, normal) { features_1.Features.position(tmpVecA, infoA); features_1.Features.position(tmpVecB, infoB); linear_algebra_1.Vec3.sub(tmpVecC, tmpVecA, tmpVecB); linear_algebra_1.Vec3.projectOnPlane(tmpVecD, tmpVecC, normal); linear_algebra_1.Vec3.add(tmpVecD, tmpVecD, tmpVecB); return linear_algebra_1.Vec3.distance(tmpVecD, tmpVecB); }; function getIonicOptions(props) { return { distanceMaxSq: props.distanceMax * props.distanceMax, }; } function getPiStackingOptions(props) { return { offsetMax: props.offsetMax, angleDevMax: (0, misc_1.degToRad)(props.angleDevMax), }; } function getCationPiOptions(props) { return { offsetMax: props.offsetMax }; } var deg180InRad = (0, misc_1.degToRad)(180); var deg90InRad = (0, misc_1.degToRad)(90); var tmpNormalA = (0, linear_algebra_1.Vec3)(); var tmpNormalB = (0, linear_algebra_1.Vec3)(); function testIonic(structure, infoA, infoB, distanceSq, opts) { var typeA = infoA.types[infoA.feature]; var typeB = infoB.types[infoB.feature]; if (isIonic(typeA, typeB)) { if (areFeaturesWithinDistanceSq(infoA, infoB, opts.distanceMaxSq)) { return 1 /* Ionic */; } } } function testPiStacking(structure, infoA, infoB, distanceSq, opts) { var typeA = infoA.types[infoA.feature]; var typeB = infoB.types[infoB.feature]; if (isPiStacking(typeA, typeB)) { getNormal(tmpNormalA, infoA); getNormal(tmpNormalB, infoB); var angle = linear_algebra_1.Vec3.angle(tmpNormalA, tmpNormalB); var offset = Math.min(getOffset(infoA, infoB, tmpNormalB), getOffset(infoB, infoA, tmpNormalA)); if (offset <= opts.offsetMax) { if (angle <= opts.angleDevMax || angle >= deg180InRad - opts.angleDevMax) { return 3 /* PiStacking */; // parallel } else if (angle <= opts.angleDevMax + deg90InRad && angle >= deg90InRad - opts.angleDevMax) { return 3 /* PiStacking */; // t-shaped } } } } function testCationPi(structure, infoA, infoB, distanceSq, opts) { var typeA = infoA.types[infoA.feature]; var typeB = infoB.types[infoB.feature]; if (isCationPi(typeA, typeB)) { var _a = typeA === 3 /* AromaticRing */ ? [infoA, infoB] : [infoB, infoA], infoR = _a[0], infoC = _a[1]; getNormal(tmpNormalA, infoR); var offset = getOffset(infoC, infoR, tmpNormalA); if (offset <= opts.offsetMax) { return 2 /* CationPi */; } } } // exports.NegativChargeProvider = features_1.Features.Provider([2 /* NegativeCharge */], addUnitNegativeCharges); exports.PositiveChargeProvider = features_1.Features.Provider([1 /* PositiveCharge */], addUnitPositiveCharges); exports.AromaticRingProvider = features_1.Features.Provider([3 /* AromaticRing */], addUnitAromaticRings); exports.IonicProvider = { name: 'ionic', params: IonicParams, createTester: function (props) { var opts = getIonicOptions(props); return { maxDistance: props.distanceMax, requiredFeatures: new Set([2 /* NegativeCharge */, 1 /* PositiveCharge */]), getType: function (structure, infoA, infoB, distanceSq) { return testIonic(structure, infoA, infoB, distanceSq, opts); } }; } }; exports.PiStackingProvider = { name: 'pi-stacking', params: PiStackingParams, createTester: function (props) { var opts = getPiStackingOptions(props); return { maxDistance: props.distanceMax, requiredFeatures: new Set([3 /* AromaticRing */]), getType: function (structure, infoA, infoB, distanceSq) { return testPiStacking(structure, infoA, infoB, distanceSq, opts); } }; } }; exports.CationPiProvider = { name: 'cation-pi', params: CationPiParams, createTester: function (props) { var opts = getCationPiOptions(props); return { maxDistance: props.distanceMax, requiredFeatures: new Set([3 /* AromaticRing */, 1 /* PositiveCharge */]), getType: function (structure, infoA, infoB, distanceSq) { return testCationPi(structure, infoA, infoB, distanceSq, opts); } }; } }; //# sourceMappingURL=charged.js.map