pdbe-molstar
Version:
Molstar implementation for PDBe
124 lines (118 loc) • 8.14 kB
JavaScript
"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
*/