UNPKG

molstar

Version:

A comprehensive macromolecular library.

223 lines (222 loc) 11 kB
"use strict"; /** * Copyright (c) 2019-2025 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.InteractionsParams = exports.ContactProviderParams = exports.Interactions = void 0; exports.computeInteractions = computeInteractions; const param_definition_1 = require("../../../mol-util/param-definition"); const structure_1 = require("../../../mol-model/structure"); const features_1 = require("./features"); const valence_model_1 = require("../valence-model"); const common_1 = require("./common"); const contacts_builder_1 = require("./contacts-builder"); const int_1 = require("../../../mol-data/int"); const contacts_1 = require("./contacts"); const halogen_bonds_1 = require("./halogen-bonds"); const hydrogen_bonds_1 = require("./hydrogen-bonds"); const charged_1 = require("./charged"); const hydrophobic_1 = require("./hydrophobic"); const set_1 = require("../../../mol-util/set"); const metal_1 = require("./metal"); const refine_1 = require("./refine"); const location_1 = require("../../../mol-model/location"); const centroid_helper_1 = require("../../../mol-math/geometry/centroid-helper"); const loci_1 = require("../../../mol-model/loci"); const label_1 = require("../../../mol-theme/label"); const type_helpers_1 = require("../../../mol-util/type-helpers"); var Interactions; (function (Interactions) { function Location(interactions, structure, unitA, indexA, unitB, indexB) { return (0, location_1.DataLocation)('interactions', { structure, interactions }, { unitA: unitA, indexA: indexA, unitB: unitB, indexB: indexB }); } Interactions.Location = Location; function isLocation(x) { return !!x && x.kind === 'data-location' && x.tag === 'interactions'; } Interactions.isLocation = isLocation; function areLocationsEqual(locA, locB) { return (locA.data.structure === locB.data.structure && locA.data.interactions === locB.data.interactions && locA.element.indexA === locB.element.indexA && locA.element.indexB === locB.element.indexB && locA.element.unitA === locB.element.unitA && locA.element.unitB === locB.element.unitB); } Interactions.areLocationsEqual = areLocationsEqual; function _label(interactions, element) { const { unitA, indexA, unitB, indexB } = element; const { contacts, unitsContacts } = interactions; if (unitA === unitB) { const contacts = unitsContacts.get(unitA.id); const idx = contacts.getDirectedEdgeIndex(indexA, indexB); return (0, common_1.interactionTypeLabel)(contacts.edgeProps.type[idx]); } else { const idx = contacts.getEdgeIndex(indexA, unitA.id, indexB, unitB.id); return (0, common_1.interactionTypeLabel)(contacts.edges[idx].props.type); } } function locationLabel(location) { return _label(location.data.interactions, location.element); } Interactions.locationLabel = locationLabel; function Loci(structure, interactions, elements) { return (0, loci_1.DataLoci)('interactions', { structure, interactions }, elements, (boundingSphere) => getBoundingSphere(interactions, elements, boundingSphere), () => getLabel(structure, interactions, elements)); } Interactions.Loci = Loci; function isLoci(x) { return !!x && x.kind === 'data-loci' && x.tag === 'interactions'; } Interactions.isLoci = isLoci; function getBoundingSphere(interactions, elements, boundingSphere) { const { unitsFeatures } = interactions; return centroid_helper_1.CentroidHelper.fromPairProvider(elements.length, (i, pA, pB) => { const e = elements[i]; features_1.Features.setPosition(pA, e.unitA, e.indexA, unitsFeatures.get(e.unitA.id)); features_1.Features.setPosition(pB, e.unitB, e.indexB, unitsFeatures.get(e.unitB.id)); }, boundingSphere); } Interactions.getBoundingSphere = getBoundingSphere; function getLabel(structure, interactions, elements) { const element = elements[0]; if (element === undefined) return ''; const { unitA, indexA, unitB, indexB } = element; const { unitsFeatures } = interactions; const { members: mA, offsets: oA } = unitsFeatures.get(unitA.id); const { members: mB, offsets: oB } = unitsFeatures.get(unitB.id); const options = { granularity: 'element' }; if (oA[indexA + 1] - oA[indexA] > 1 || oB[indexB + 1] - oB[indexB] > 1) { options.granularity = 'residue'; } return [ _label(interactions, element), (0, label_1.bondLabel)(structure_1.Bond.Location(structure, unitA, mA[oA[indexA]], structure, unitB, mB[oB[indexB]]), options) ].join('</br>'); } Interactions.getLabel = getLabel; })(Interactions || (exports.Interactions = Interactions = {})); const FeatureProviders = [ hydrogen_bonds_1.HydrogenDonorProvider, hydrogen_bonds_1.WeakHydrogenDonorProvider, hydrogen_bonds_1.HydrogenAcceptorProvider, charged_1.NegativChargeProvider, charged_1.PositiveChargeProvider, charged_1.AromaticRingProvider, halogen_bonds_1.HalogenDonorProvider, halogen_bonds_1.HalogenAcceptorProvider, hydrophobic_1.HydrophobicAtomProvider, metal_1.MetalProvider, metal_1.MetalBindingProvider, ]; const ContactProviders = { 'ionic': charged_1.IonicProvider, 'pi-stacking': charged_1.PiStackingProvider, 'cation-pi': charged_1.CationPiProvider, 'halogen-bonds': halogen_bonds_1.HalogenBondsProvider, 'hydrogen-bonds': hydrogen_bonds_1.HydrogenBondsProvider, 'weak-hydrogen-bonds': hydrogen_bonds_1.WeakHydrogenBondsProvider, 'hydrophobic': hydrophobic_1.HydrophobicProvider, 'metal-coordination': metal_1.MetalCoordinationProvider, }; function getProvidersParams(defaultOn = []) { const params = Object.create(null); Object.keys(ContactProviders).forEach(k => { params[k] = param_definition_1.ParamDefinition.MappedStatic(defaultOn.includes(k) ? 'on' : 'off', { on: param_definition_1.ParamDefinition.Group(ContactProviders[k].params), off: param_definition_1.ParamDefinition.Group({}) }, { cycle: true }); }); return params; } exports.ContactProviderParams = getProvidersParams([ // 'ionic', 'cation-pi', 'pi-stacking', 'hydrogen-bonds', 'halogen-bonds', // 'hydrophobic', 'metal-coordination', // 'weak-hydrogen-bonds', ]); exports.InteractionsParams = { providers: param_definition_1.ParamDefinition.Group(exports.ContactProviderParams, { isFlat: true }), contacts: param_definition_1.ParamDefinition.Group(contacts_1.ContactsParams, { label: 'Advanced Options' }), }; async function computeInteractions(ctx, structure, props, options) { const p = { ...param_definition_1.ParamDefinition.getDefaultValues(exports.InteractionsParams), ...props }; const cacheKey = JSON.stringify(p); await valence_model_1.ValenceModelProvider.attach(ctx, structure); const contactTesters = []; (0, type_helpers_1.ObjectKeys)(ContactProviders).forEach(k => { const { name, params } = p.providers[k]; if (name === 'on') { contactTesters.push(ContactProviders[k].createTester(params)); } }); const requiredFeatures = new Set(); contactTesters.forEach(l => set_1.SetUtils.add(requiredFeatures, l.requiredFeatures)); const featureProviders = FeatureProviders.filter(f => set_1.SetUtils.areIntersecting(requiredFeatures, f.types)); const unitsFeatures = int_1.IntMap.Mutable(); const unitsContacts = int_1.IntMap.Mutable(); for (let i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) { const group = structure.unitSymmetryGroups[i]; if (ctx.runtime.shouldUpdate) { await ctx.runtime.update({ message: 'computing interactions', current: i, max: il }); } const features = findUnitFeatures(structure, group.units[0], featureProviders, cacheKey); for (let j = 0, jl = group.units.length; j < jl; ++j) { const u = group.units[j]; unitsFeatures.set(u.id, features); } if (options === null || options === void 0 ? void 0 : options.skipIntraContacts) continue; const intraUnitContacts = findIntraUnitContacts(structure, group.units[0], features, contactTesters, p.contacts); for (let j = 0, jl = group.units.length; j < jl; ++j) { const u = group.units[j]; unitsContacts.set(u.id, intraUnitContacts); } } const contacts = findInterUnitContacts(structure, unitsFeatures, contactTesters, p.contacts, options); const interactions = { unitsFeatures, unitsContacts, contacts }; (0, refine_1.refineInteractions)(structure, interactions); return interactions; } function findUnitFeatures(structure, unit, featureProviders, cacheKey) { const key = `features-${cacheKey}`; if (unit.transientCache.has(key)) { return unit.transientCache.get(key); } const count = unit.elements.length; const featuresBuilder = features_1.FeaturesBuilder.create(count, count / 2); if (structure_1.Unit.isAtomic(unit)) { for (const fp of featureProviders) { fp.add(structure, unit, featuresBuilder); } } const features = featuresBuilder.getFeatures(count); unit.transientCache.set(key, features); return features; } function findIntraUnitContacts(structure, unit, features, contactTesters, props) { const builder = contacts_builder_1.IntraContactsBuilder.create(features, unit.elements.length); if (structure_1.Unit.isAtomic(unit)) { (0, contacts_1.addUnitContacts)(structure, unit, features, builder, contactTesters, props); } return builder.getContacts(); } function findInterUnitContacts(structure, unitsFeatures, contactTesters, props, options) { const builder = contacts_builder_1.InterContactsBuilder.create(); structure_1.Structure.eachUnitPair(structure, (unitA, unitB) => { const featuresA = unitsFeatures.get(unitA.id); const featuresB = unitsFeatures.get(unitB.id); (0, contacts_1.addStructureContacts)(structure, unitA, featuresA, unitB, featuresB, builder, contactTesters, props); }, { maxRadius: Math.max(...contactTesters.map(t => t.maxDistance)), validUnit: (unit) => structure_1.Unit.isAtomic(unit), validUnitPair: (unitA, unitB) => { if (options === null || options === void 0 ? void 0 : options.unitPairTest) return options.unitPairTest(unitA, unitB); return structure_1.Structure.validUnitPair(structure, unitA, unitB); } }); return builder.getContacts(unitsFeatures); }