UNPKG

pdbe-molstar

Version:
124 lines (118 loc) 8.14 kB
"use strict"; /** Helper functions to allow superposition of complexes */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Coloring = void 0; exports.loadComplexSuperposition = loadComplexSuperposition; exports.superposeStructuresByMolstarDefault = superposeStructuresByMolstarDefault; const tslib_1 = require("tslib"); const superposition_sifts_mapping_1 = require("molstar/lib/mol-model/structure/structure/util/superposition-sifts-mapping"); const commands_1 = require("molstar/lib/mol-plugin/commands"); const sleep_1 = require("molstar/lib/mol-util/sleep"); const __1 = require("../.."); const helpers_1 = require("../../helpers"); const superposition_1 = require("../../superposition"); const Coloring = tslib_1.__importStar(require("./coloring")); const superpose_by_biggest_chain_1 = require("./superpose-by-biggest-chain"); exports.Coloring = tslib_1.__importStar(require("./coloring")); /** Load a structure, superpose onto the main structure based on Uniprot residue numbers, and optionally apply coloring to show common/additional components. */ function loadComplexSuperposition(viewer, params) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; const { pdbId, assemblyId, animationDuration = 250, coloring, baseComponents, otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors, method = 'biggest-matched-chain' } = params; const baseStructId = __1.PDBeMolstarPlugin.MAIN_STRUCTURE_ID; const otherStructId = (_a = params.id) !== null && _a !== void 0 ? _a : `${pdbId}_${assemblyId}`; // Apply coloring to base structure if (coloring) { if (!baseComponents) throw new Error('`baseComponents` is required when `coloring` is not `undefined`'); if (!otherComponents) throw new Error('`otherComponents` is required when `coloring` is not `undefined`'); } if (coloring === 'subcomplex') { yield Coloring.colorSubcomplex(viewer, { baseStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors }); } if (coloring === 'supercomplex') { yield Coloring.colorSupercomplex(viewer, { baseStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors }); } // Load other structure const otherStructUrl = (0, helpers_1.getStructureUrl)(viewer.initParams, { pdbId, queryType: 'full' }); yield viewer.load({ url: otherStructUrl, isBinary: viewer.initParams.encoding === 'bcif', assemblyId, id: otherStructId }, false); yield viewer.visual.structureVisibility(otherStructId, false); // hide structure until superposition complete, to avoid flickering const baseStruct = (_c = (_b = viewer.getStructure(baseStructId)) === null || _b === void 0 ? void 0 : _b.cell.obj) === null || _c === void 0 ? void 0 : _c.data; if (!baseStruct) throw new Error('Static structure not loaded'); const otherStruct = (_e = (_d = viewer.getStructure(otherStructId)) === null || _d === void 0 ? void 0 : _d.cell.obj) === null || _e === void 0 ? void 0 : _e.data; if (!otherStruct) throw new Error('Mobile structure not loaded'); // Superpose other structure on base structure const { status, superposition } = method === 'biggest-matched-chain' ? (0, superpose_by_biggest_chain_1.superposeStructuresByBiggestCommonChain)(baseStruct, otherStruct, baseComponents, otherComponents) : superposeStructuresByMolstarDefault(baseStruct, otherStruct); if (superposition) { yield (0, superposition_1.transform)(viewer.plugin, viewer.getStructure(otherStructId).cell, superposition.bTransform); } // Apply coloring to other structure if (coloring === 'subcomplex') { yield Coloring.colorSubcomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors }); } if (coloring === 'supercomplex') { yield Coloring.colorSupercomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors }); } // Adjust camera const staticStructRef = viewer.getStructure(baseStructId); const mobileStructRef = viewer.getStructure(otherStructId); // it is important to run this after superposition, to get correct coordinates for camera adjustment // Wanted to use PluginCommands.Camera.Focus with viewer.plugin.canvas3d?.boundingSphere, but seems to not work properly, so using the following workaround: yield commands_1.PluginCommands.Camera.FocusObject(viewer.plugin, { targets: [ { targetRef: staticStructRef === null || staticStructRef === void 0 ? void 0 : staticStructRef.cell.transform.ref, extraRadius: 0.3 }, { targetRef: (_g = ((_f = mobileStructRef === null || mobileStructRef === void 0 ? void 0 : mobileStructRef.transform) !== null && _f !== void 0 ? _f : mobileStructRef)) === null || _g === void 0 ? void 0 : _g.cell.transform.ref, extraRadius: 0.3 }, // 0.3 is amount by which bounding sphere algorithm usually underestimates actual visible bounding sphere ], durationMs: animationDuration, }); yield (0, sleep_1.sleep)(animationDuration); // wait until camera adjustment completed // Reveal new structure yield viewer.visual.structureVisibility(otherStructId, true); return { /** Structure identifier of the newly loaded complex structure, to refer to this structure later */ id: otherStructId, /** Status of pairwise superposition ('success' / 'zero-overlap' / 'failed') */ status, /** Superposition RMSD and tranform (if status is 'success') */ superposition, /** Function that deletes the newly loaded complex structure */ delete: () => viewer.deleteStructure(otherStructId), }; }); } /** Superpose mobile structure onto static structure, based on Uniprot residue numbers. */ function superposeStructuresByMolstarDefault(staticStruct, mobileStruct) { var _a; const aln = (0, superposition_sifts_mapping_1.alignAndSuperposeWithSIFTSMapping)([staticStruct, mobileStruct], { traceOnly: true }); const superposition = (_a = aln.entries.find(e => e.pivot === 0 && e.other === 1)) === null || _a === void 0 ? void 0 : _a.transform; if (superposition) { return { status: 'success', superposition: Object.assign(Object.assign({}, superposition), { nAlignedElements: Number.NaN }) }; } else if (aln.zeroOverlapPairs.find(e => e[0] === 0 && e[1] === 1)) { return { status: 'zero-overlap', superposition: undefined }; } else { return { status: 'failed', superposition: undefined }; } } /* Coloring - subcomplexes: - base common -> by entity, lighter - base additional -> gray, lighter - sub common -> by entity, darker - sub additional -> gray, darker (these are all unmapped components, includes antibodies and ligands) -> Colors can be assigned based on base complex and applied to subcomplex Coloring - supercomplexes: - base common -> gray, lighter - base additional -> unmapped color, lighter (these are all unmapped components, includes antibodies and ligands) - super common -> gray, darker - super additional mapped -> by entity, darker - super additional unmapped -> unmapped color, darker -> Colors can be assigned based on supercomplex complex, consistency between supercomplexes is probably not necessary -> For both subcomplexes and supercomplexes, colors could be assigned based on UniprotID hash -> database-wide consistency but complexes with similar-color components will occur */