pdbe-molstar 3.5.0 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/build/pdbe-molstar-component.js +2 -2
  2. package/build/pdbe-molstar-light.css +2 -2
  3. package/build/pdbe-molstar-plugin.js +2 -2
  4. package/build/pdbe-molstar-plugin.js.LICENSE.txt +1 -1
  5. package/build/pdbe-molstar.css +2 -2
  6. package/lib/alphafold-transparency.js +7 -8
  7. package/lib/extensions/complexes/coloring.d.ts +55 -0
  8. package/lib/extensions/complexes/coloring.js +129 -0
  9. package/lib/extensions/complexes/index.d.ts +70 -0
  10. package/lib/extensions/complexes/index.js +99 -0
  11. package/lib/extensions/complexes/superpose-by-biggest-chain.d.ts +15 -0
  12. package/lib/extensions/complexes/superpose-by-biggest-chain.js +141 -0
  13. package/lib/extensions/complexes/superpose-by-sequence-alignment.d.ts +10 -0
  14. package/lib/extensions/complexes/superpose-by-sequence-alignment.js +181 -0
  15. package/lib/helpers.d.ts +15 -1
  16. package/lib/helpers.js +119 -68
  17. package/lib/plugin-custom-state.d.ts +0 -2
  18. package/lib/sequence-color/behavior.d.ts +5 -0
  19. package/lib/sequence-color/behavior.js +54 -0
  20. package/lib/sequence-color/color.d.ts +11 -0
  21. package/lib/sequence-color/color.js +58 -0
  22. package/lib/sequence-color/prop.d.ts +38 -0
  23. package/lib/sequence-color/prop.js +38 -0
  24. package/lib/spec.d.ts +1 -1
  25. package/lib/styles/pdbe-molstar/_index.scss +7 -7
  26. package/lib/styles/pdbe-molstar-dark.scss +2 -2
  27. package/lib/styles/pdbe-molstar-light.scss +2 -2
  28. package/lib/superposition.d.ts +2 -2
  29. package/lib/superposition.js +41 -60
  30. package/lib/ui/alphafold-superposition.js +2 -2
  31. package/lib/ui/pdbe-viewport-controls.js +3 -4
  32. package/lib/ui/pdbe-viewport.d.ts +5 -1
  33. package/lib/ui/pdbe-viewport.js +23 -2
  34. package/lib/ui/split-ui/components.d.ts +1 -1
  35. package/lib/ui/split-ui/components.js +2 -2
  36. package/lib/viewer.d.ts +7 -1
  37. package/lib/viewer.js +27 -14
  38. package/package.json +3 -3
  39. package/lib/superposition-sifts-mapping.d.ts +0 -22
  40. package/lib/superposition-sifts-mapping.js +0 -153
@@ -0,0 +1,70 @@
1
+ /** Helper functions to allow superposition of complexes */
2
+ import { MinimizeRmsd } from 'molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd';
3
+ import { PDBeMolstarPlugin } from '../..';
4
+ import { QueryParam } from '../../helpers';
5
+ export * as Coloring from './coloring';
6
+ /** Parameters to `loadComplexSuperposition` */
7
+ export interface LoadComplexSuperpositionParams {
8
+ /** PDB identifier of complex structure */
9
+ pdbId: string;
10
+ /** Assembly identifier */
11
+ assemblyId: string;
12
+ /** Arbitrary string identifier to refer to the newly loaded structure later */
13
+ id?: string;
14
+ /** Duration of camera transition, in milliseconds. Default: 250. Do not use 0 (won't work because of bugs in Molstar), but you can use 1. */
15
+ animationDuration?: number;
16
+ /** Apply coloring to the base structure and newly loaded structure.
17
+ * 'subcomplex' = color the common components by unique entities (UniProt/Rfam accession); color remaining components by base color (gray).
18
+ * 'supercomplex' = color the common components by base color (gray); color additional components in the new structure (supercomplex) by unique entities. */
19
+ coloring?: 'subcomplex' | 'supercomplex' | undefined;
20
+ /** List of Uniprot/Rfam accessions in the base complex. Mandatory when `coloring` is not `undefined`. */
21
+ baseComponents?: string[];
22
+ /** List of Uniprot/Rfam accessions in the newly loaded complex. Mandatory when `coloring` is not `undefined`. */
23
+ otherComponents?: string[];
24
+ /** Base color for coloring "uninteresting" parts of structures (i.e. subcomplexes: additional components, supercomplexes: common components). Default: gray. */
25
+ coreColor?: string;
26
+ /** Color for coloring unmapped additional components for supercomplexes. */
27
+ unmappedColor?: string;
28
+ /** List of colors for coloring unique entities. */
29
+ componentColors?: string[];
30
+ /** Optional specification of which parts of the base complex structure belong to individual Uniprot/Rfam accessions (will be inferred from mmCIF atom_site if not provided).
31
+ * It is recommended to provide this for nucleic acids as they don't have mapping in atom_site. */
32
+ baseMappings?: {
33
+ [accession: string]: QueryParam[];
34
+ };
35
+ /** Optional specification of which parts of the other complex structure belong to individual Uniprot/Rfam accessions (will be inferred from mmCIF atom_site if not provided).
36
+ * It is recommended to provide this for nucleic acids as they don't have mapping in atom_site. */
37
+ otherMappings?: {
38
+ [accession: string]: QueryParam[];
39
+ };
40
+ }
41
+ /** Result type of `loadComplexSuperposition` */
42
+ export interface LoadComplexSuperpositionResult {
43
+ /** Structure identifier of the newly loaded complex structure, to refer to this structure later */
44
+ id: string;
45
+ /** Superposition RMSD, number of aligned residues, and tranform; if superposition was successful */
46
+ superposition: SuperpositionResult | undefined;
47
+ /** Function that deletes the newly loaded complex structure */
48
+ delete: () => Promise<void>;
49
+ }
50
+ /** Temporary type until `nAlignedElements` gets into `MinimizeRmsd.Result` in core Molstar */
51
+ export interface SuperpositionResult extends MinimizeRmsd.Result {
52
+ nAlignedElements: number;
53
+ method: 'uniprot-numbering' | 'sequence-alignment';
54
+ accession: string;
55
+ }
56
+ /** Load a structure, superpose onto the main structure based on Uniprot residue numbers (or seq alignment if numbers not available),
57
+ * and optionally apply coloring to show common/additional components.
58
+ *
59
+ * **Superposition method:**
60
+ * Complexes are superposed based on the largest common component (measured the by number of residues) with a UniProt mapping (taken from atom_site mmCIF category).
61
+ * In case there are no common components with Uniprot mapping, the largest common component with an Rfam mapping is used (taken from `baseMappings` and `otherMappings` parameters).
62
+ * Residue-residue correspondence is determined by UniProt residue numbers (for UniProt mappings) or by sequence alignment (for Rfam mappings).
63
+ *
64
+ * **Coloring - subcomplexes:**
65
+ * Common components are colored by entity; additional components are gray. Components in the subcomplex are slightly darkened.
66
+ *
67
+ * **Coloring - supercomplexes:**
68
+ * Common components are gray; mapped additional components are colored by entity; unmapped additional components are magenta. Components in the supercomplex are slightly darkened.
69
+ */
70
+ export declare function loadComplexSuperposition(viewer: PDBeMolstarPlugin, params: LoadComplexSuperpositionParams): Promise<LoadComplexSuperpositionResult>;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /** Helper functions to allow superposition of complexes */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.Coloring = void 0;
5
+ exports.loadComplexSuperposition = loadComplexSuperposition;
6
+ const tslib_1 = require("tslib");
7
+ const commands_1 = require("molstar/lib/mol-plugin/commands");
8
+ const sleep_1 = require("molstar/lib/mol-util/sleep");
9
+ const __1 = require("../..");
10
+ const helpers_1 = require("../../helpers");
11
+ const superposition_1 = require("../../superposition");
12
+ const Coloring = tslib_1.__importStar(require("./coloring"));
13
+ const superpose_by_biggest_chain_1 = require("./superpose-by-biggest-chain");
14
+ const superpose_by_sequence_alignment_1 = require("./superpose-by-sequence-alignment");
15
+ exports.Coloring = tslib_1.__importStar(require("./coloring"));
16
+ /** Load a structure, superpose onto the main structure based on Uniprot residue numbers (or seq alignment if numbers not available),
17
+ * and optionally apply coloring to show common/additional components.
18
+ *
19
+ * **Superposition method:**
20
+ * Complexes are superposed based on the largest common component (measured the by number of residues) with a UniProt mapping (taken from atom_site mmCIF category).
21
+ * In case there are no common components with Uniprot mapping, the largest common component with an Rfam mapping is used (taken from `baseMappings` and `otherMappings` parameters).
22
+ * Residue-residue correspondence is determined by UniProt residue numbers (for UniProt mappings) or by sequence alignment (for Rfam mappings).
23
+ *
24
+ * **Coloring - subcomplexes:**
25
+ * Common components are colored by entity; additional components are gray. Components in the subcomplex are slightly darkened.
26
+ *
27
+ * **Coloring - supercomplexes:**
28
+ * Common components are gray; mapped additional components are colored by entity; unmapped additional components are magenta. Components in the supercomplex are slightly darkened.
29
+ */
30
+ function loadComplexSuperposition(viewer, params) {
31
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
32
+ var _a, _b, _c, _d, _e, _f, _g;
33
+ const { pdbId, assemblyId, animationDuration = 250, coloring, baseComponents, otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors } = params;
34
+ const baseStructId = __1.PDBeMolstarPlugin.MAIN_STRUCTURE_ID;
35
+ const otherStructId = (_a = params.id) !== null && _a !== void 0 ? _a : `${pdbId}_${assemblyId}`;
36
+ // Apply coloring to base structure
37
+ if (coloring) {
38
+ if (!baseComponents)
39
+ throw new Error('`baseComponents` is required when `coloring` is not `undefined`');
40
+ if (!otherComponents)
41
+ throw new Error('`otherComponents` is required when `coloring` is not `undefined`');
42
+ }
43
+ if (coloring === 'subcomplex') {
44
+ yield Coloring.colorSubcomplex(viewer, { baseStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors });
45
+ }
46
+ if (coloring === 'supercomplex') {
47
+ yield Coloring.colorSupercomplex(viewer, { baseStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors });
48
+ }
49
+ // Load other structure
50
+ const otherStructUrl = (0, helpers_1.getStructureUrl)(viewer.initParams, { pdbId, queryType: 'full' });
51
+ yield viewer.load({ url: otherStructUrl, isBinary: viewer.initParams.encoding === 'bcif', assemblyId, id: otherStructId }, false);
52
+ yield viewer.visual.structureVisibility(otherStructId, false); // hide structure until superposition complete, to avoid flickering
53
+ const baseStruct = (_c = (_b = viewer.getStructure(baseStructId)) === null || _b === void 0 ? void 0 : _b.cell.obj) === null || _c === void 0 ? void 0 : _c.data;
54
+ if (!baseStruct)
55
+ throw new Error('Static structure not loaded');
56
+ const otherStruct = (_e = (_d = viewer.getStructure(otherStructId)) === null || _d === void 0 ? void 0 : _d.cell.obj) === null || _e === void 0 ? void 0 : _e.data;
57
+ if (!otherStruct)
58
+ throw new Error('Mobile structure not loaded');
59
+ // Superpose other structure on base structure
60
+ let superposition = (0, superpose_by_biggest_chain_1.superposeByBiggestCommonChain)(baseStruct, otherStruct, baseComponents, otherComponents);
61
+ if (!superposition) {
62
+ console.log(`UniProt-based superposition method failed, trying sequence alignment superposition (RNAs)`);
63
+ superposition = (0, superpose_by_sequence_alignment_1.superposeBySequenceAlignment)(baseStruct, otherStruct, baseMappings !== null && baseMappings !== void 0 ? baseMappings : {}, otherMappings !== null && otherMappings !== void 0 ? otherMappings : {});
64
+ }
65
+ if (superposition) {
66
+ yield (0, superposition_1.transform)(viewer.plugin, viewer.getStructure(otherStructId).cell, superposition.bTransform);
67
+ }
68
+ else {
69
+ console.log(`Sequence alignment superposition method failed, leaving unsuperposed`);
70
+ }
71
+ // Apply coloring to other structure
72
+ if (coloring === 'subcomplex') {
73
+ yield Coloring.colorSubcomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors });
74
+ }
75
+ if (coloring === 'supercomplex') {
76
+ yield Coloring.colorSupercomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors });
77
+ }
78
+ // Adjust camera
79
+ const staticStructRef = viewer.getStructure(baseStructId);
80
+ const mobileStructRef = viewer.getStructure(otherStructId); // it is important to run this after superposition, to get correct coordinates for camera adjustment
81
+ // Wanted to use PluginCommands.Camera.Focus with viewer.plugin.canvas3d?.boundingSphere, but seems to not work properly, so using the following workaround:
82
+ yield commands_1.PluginCommands.Camera.FocusObject(viewer.plugin, {
83
+ targets: [
84
+ { targetRef: staticStructRef === null || staticStructRef === void 0 ? void 0 : staticStructRef.cell.transform.ref, extraRadius: 0.3 },
85
+ { 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 },
86
+ // 0.3 is amount by which bounding sphere algorithm usually underestimates actual visible bounding sphere
87
+ ],
88
+ durationMs: animationDuration,
89
+ });
90
+ yield (0, sleep_1.sleep)(animationDuration); // wait until camera adjustment completed
91
+ // Reveal new structure
92
+ yield viewer.visual.structureVisibility(otherStructId, true);
93
+ return {
94
+ id: otherStructId,
95
+ superposition,
96
+ delete: () => viewer.deleteStructure(otherStructId),
97
+ };
98
+ });
99
+ }
@@ -0,0 +1,15 @@
1
+ import { Structure } from 'molstar/lib/mol-model/structure';
2
+ import { SuperpositionResult } from './index';
3
+ /** Superpose structures based on the largest common component (measured the by number of residues) with a UniProt mapping (taken from atom_site mmCIF category).
4
+ * Residue-residue correspondence is determined by UniProt residue numbers.
5
+ * This differs from UniProt-based superposition in core Molstar (`alignAndSuperposeWithSIFTSMapping`) which takes all components (regardless of their spacial arrangement). */
6
+ export declare function superposeByBiggestCommonChain(structA: Structure, structB: Structure, allowedComponentsA: string[] | undefined, allowedComponentsB: string[] | undefined): SuperpositionResult | undefined;
7
+ export interface SortedAccessionsAndUnits<T extends object = object> {
8
+ accessions: string[];
9
+ units: {
10
+ [accession: string]: ({
11
+ unitId: string;
12
+ size: number;
13
+ } & T)[];
14
+ };
15
+ }
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.superposeByBiggestCommonChain = superposeByBiggestCommonChain;
4
+ const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd");
5
+ const mmcif_1 = require("molstar/lib/mol-model-formats/structure/mmcif");
6
+ /** Superpose structures based on the largest common component (measured the by number of residues) with a UniProt mapping (taken from atom_site mmCIF category).
7
+ * Residue-residue correspondence is determined by UniProt residue numbers.
8
+ * This differs from UniProt-based superposition in core Molstar (`alignAndSuperposeWithSIFTSMapping`) which takes all components (regardless of their spacial arrangement). */
9
+ function superposeByBiggestCommonChain(structA, structB, allowedComponentsA, allowedComponentsB) {
10
+ const indexA = extractUniprotIndex(structA, allowedComponentsA);
11
+ const indexB = extractUniprotIndex(structB, allowedComponentsB);
12
+ const bestMatch = bestUniprotMatch(indexA, indexB);
13
+ if (!bestMatch) {
14
+ return undefined;
15
+ }
16
+ const unitA = structA.unitMap.get(Number(bestMatch.unitA));
17
+ const unitB = structB.unitMap.get(Number(bestMatch.unitB));
18
+ const unitIndexA = indexA[bestMatch.accession][bestMatch.unitA];
19
+ const unitIndexB = indexB[bestMatch.accession][bestMatch.unitB];
20
+ const positionsA = minimize_rmsd_1.MinimizeRmsd.Positions.empty(bestMatch.nMatchedElements);
21
+ const positionsB = minimize_rmsd_1.MinimizeRmsd.Positions.empty(bestMatch.nMatchedElements);
22
+ let i = 0;
23
+ for (const unpNum in unitIndexA.atomMap) {
24
+ const iAtomB = unitIndexB.atomMap[unpNum];
25
+ if (iAtomB === undefined)
26
+ continue;
27
+ const iAtomA = unitIndexA.atomMap[unpNum];
28
+ positionsA.x[i] = unitA.conformation.coordinates.x[iAtomA];
29
+ positionsA.y[i] = unitA.conformation.coordinates.y[iAtomA];
30
+ positionsA.z[i] = unitA.conformation.coordinates.z[iAtomA];
31
+ positionsB.x[i] = unitB.conformation.coordinates.x[iAtomB];
32
+ positionsB.y[i] = unitB.conformation.coordinates.y[iAtomB];
33
+ positionsB.z[i] = unitB.conformation.coordinates.z[iAtomB];
34
+ i++;
35
+ }
36
+ const superposition = minimize_rmsd_1.MinimizeRmsd.compute({ a: positionsA, b: positionsB });
37
+ if (!isNaN(superposition.rmsd)) {
38
+ return Object.assign(Object.assign({}, superposition), { nAlignedElements: bestMatch.nMatchedElements, method: 'uniprot-numbering', accession: bestMatch.accession });
39
+ // TODO remove explicit nAlignedElements, once in core Molstar
40
+ }
41
+ else {
42
+ return undefined;
43
+ }
44
+ }
45
+ function extractUniprotIndex(structure, allowedAccessions) {
46
+ var _a, _b, _c;
47
+ var _d, _e;
48
+ const allowedAccessionsSet = allowedAccessions ? new Set(allowedAccessions) : undefined;
49
+ const seenUnitInvariantIds = new Set();
50
+ const out = {};
51
+ for (const unit of structure.units) {
52
+ if (seenUnitInvariantIds.has(unit.invariantId))
53
+ continue;
54
+ else
55
+ seenUnitInvariantIds.add(unit.invariantId);
56
+ const src = structure.model.sourceData;
57
+ if (!mmcif_1.MmcifFormat.is(src))
58
+ throw new Error('Source data must be mmCIF/BCIF');
59
+ const h = unit.model.atomicHierarchy;
60
+ const { pdbx_sifts_xref_db_acc, pdbx_sifts_xref_db_name, pdbx_sifts_xref_db_num } = src.data.db.atom_site;
61
+ const atoms = unit.polymerElements;
62
+ const nAtoms = atoms.length;
63
+ for (let i = 0; i < nAtoms; i++) {
64
+ const iAtom = atoms[i];
65
+ const srcIAtom = h.atomSourceIndex.value(iAtom);
66
+ const dbName = pdbx_sifts_xref_db_name.value(srcIAtom);
67
+ if (dbName !== 'UNP')
68
+ continue;
69
+ const dbAcc = pdbx_sifts_xref_db_acc.value(srcIAtom);
70
+ if (allowedAccessionsSet && !allowedAccessionsSet.has(dbAcc))
71
+ continue;
72
+ const dbNum = pdbx_sifts_xref_db_num.value(srcIAtom);
73
+ const structMapping = (_a = out[dbAcc]) !== null && _a !== void 0 ? _a : (out[dbAcc] = {});
74
+ const chainMapping = (_b = structMapping[_d = unit.id]) !== null && _b !== void 0 ? _b : (structMapping[_d] = {
75
+ label_asym_id: h.chains.label_asym_id.value(h.chainAtomSegments.index[atoms[0]]),
76
+ auth_asym_id: h.chains.auth_asym_id.value(h.chainAtomSegments.index[atoms[0]]),
77
+ atomMap: {},
78
+ });
79
+ (_c = (_e = chainMapping.atomMap)[dbNum]) !== null && _c !== void 0 ? _c : (_e[dbNum] = iAtom);
80
+ }
81
+ }
82
+ return out;
83
+ }
84
+ /** Sort units for each accession by decreasing size and sort accessions by decreasing biggest unit size. */
85
+ function sortAccessionsAndUnits(uniprotIndex) {
86
+ const unitsByAccession = {};
87
+ for (const accession in uniprotIndex) {
88
+ const unitIds = uniprotIndex[accession];
89
+ const units = [];
90
+ for (const unitId in unitIds) {
91
+ const size = Object.keys(unitIds[unitId].atomMap).length;
92
+ units.push({ unitId, size });
93
+ }
94
+ units.sort((a, b) => b.size - a.size);
95
+ unitsByAccession[accession] = units;
96
+ }
97
+ return {
98
+ /** Accessions sorted by decreasing biggest unit size */
99
+ accessions: Object.keys(unitsByAccession).sort((a, b) => unitsByAccession[b][0].size - unitsByAccession[a][0].size),
100
+ /** Units per accession, sorted by decreasing unit size */
101
+ units: unitsByAccession,
102
+ };
103
+ }
104
+ function bestUniprotMatch(a, b) {
105
+ const sortedA = sortAccessionsAndUnits(a);
106
+ const sortedB = sortAccessionsAndUnits(b);
107
+ let bestMatch = undefined;
108
+ let bestScore = 0;
109
+ for (const accession of sortedA.accessions) {
110
+ const unitsA = sortedA.units[accession];
111
+ const unitsB = sortedB.units[accession];
112
+ if (!unitsB)
113
+ continue;
114
+ for (const ua of unitsA) {
115
+ if (ua.size <= bestScore)
116
+ break;
117
+ const unitA = a[accession][ua.unitId];
118
+ for (const ub of unitsB) {
119
+ if (ub.size <= bestScore || ua.size <= bestScore)
120
+ break;
121
+ const unitB = b[accession][ub.unitId];
122
+ const score = objectKeyOverlap(unitA.atomMap, unitB.atomMap);
123
+ if (score > bestScore) {
124
+ bestScore = score;
125
+ bestMatch = { accession, unitA: ua.unitId, unitB: ub.unitId, nMatchedElements: score };
126
+ }
127
+ }
128
+ }
129
+ }
130
+ return bestMatch;
131
+ }
132
+ /** Return number of keys common to objects `a` and `b` */
133
+ function objectKeyOverlap(a, b) {
134
+ let overlap = 0;
135
+ for (const key in a) {
136
+ if (key in b) {
137
+ overlap++;
138
+ }
139
+ }
140
+ return overlap;
141
+ }
@@ -0,0 +1,10 @@
1
+ import { Structure } from 'molstar/lib/mol-model/structure';
2
+ import { QueryParam } from '../../helpers';
3
+ import { SuperpositionResult } from './index';
4
+ /** Superpose structures based on the largest common component (measured the by number of residues), components being defined by `mappingsA` and `mappingsB`.
5
+ * Residue-residue correspondence is determined by sequence alignment. */
6
+ export declare function superposeBySequenceAlignment(structA: Structure, structB: Structure, mappingsA: {
7
+ [accession: string]: QueryParam[];
8
+ }, mappingsB: {
9
+ [accession: string]: QueryParam[];
10
+ }): SuperpositionResult | undefined;
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.superposeBySequenceAlignment = superposeBySequenceAlignment;
4
+ const int_1 = require("molstar/lib/mol-data/int");
5
+ const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd");
6
+ const alignment_1 = require("molstar/lib/mol-model/sequence/alignment/alignment");
7
+ const structure_1 = require("molstar/lib/mol-model/structure");
8
+ const superposition_1 = require("molstar/lib/mol-model/structure/structure/util/superposition");
9
+ const helpers_1 = require("../../helpers");
10
+ /** Superpose structures based on the largest common component (measured the by number of residues), components being defined by `mappingsA` and `mappingsB`.
11
+ * Residue-residue correspondence is determined by sequence alignment. */
12
+ function superposeBySequenceAlignment(structA, structB, mappingsA, mappingsB) {
13
+ const sortedA = sortAccessionsAndUnits(structA, mappingsA);
14
+ const sortedB = sortAccessionsAndUnits(structB, mappingsB);
15
+ const bestMatch = bestMappingMatch(sortedA, sortedB);
16
+ if (!bestMatch) {
17
+ return undefined;
18
+ }
19
+ const accession = bestMatch.accession;
20
+ const lociA = helpers_1.QueryHelper.getInteractivityLoci(mappingsA[accession], structA);
21
+ const lociB = helpers_1.QueryHelper.getInteractivityLoci(mappingsB[accession], structB);
22
+ const superposition = alignAndSuperpose(lociA, lociB);
23
+ if (!isNaN(superposition.rmsd)) {
24
+ return Object.assign(Object.assign({}, superposition), { method: 'sequence-alignment', accession: bestMatch.accession });
25
+ }
26
+ else {
27
+ return undefined;
28
+ }
29
+ }
30
+ /** Sort units for each accession by decreasing size and sort accessions by decreasing biggest unit size. */
31
+ function sortAccessionsAndUnits(struct, mappings) {
32
+ const unitsByAccession = {};
33
+ for (const accession in mappings) {
34
+ const loci = helpers_1.QueryHelper.getInteractivityLoci(mappings[accession], struct);
35
+ const units = [];
36
+ for (const u of loci.elements) {
37
+ const unitId = u.unit.id.toString();
38
+ const elements = [];
39
+ int_1.OrderedSet.forEach(u.indices, elementUnitIndex => {
40
+ const elementIndex = u.unit.elements[elementUnitIndex];
41
+ if (int_1.SortedArray.has(u.unit.polymerElements, elementIndex))
42
+ elements.push(elementIndex);
43
+ });
44
+ units.push({ unitId, size: elements.length, elements: int_1.SortedArray.ofSortedArray(elements) });
45
+ }
46
+ units.sort((a, b) => b.size - a.size);
47
+ unitsByAccession[accession] = units;
48
+ }
49
+ return {
50
+ /** Accessions sorted by decreasing biggest unit size */
51
+ accessions: Object.keys(unitsByAccession).sort((a, b) => unitsByAccession[b][0].size - unitsByAccession[a][0].size),
52
+ /** Units per accession, sorted by decreasing unit size */
53
+ units: unitsByAccession,
54
+ };
55
+ }
56
+ function bestMappingMatch(sortedA, sortedB) {
57
+ let bestMatch = undefined;
58
+ let bestScore = 0;
59
+ for (const accession of sortedA.accessions) {
60
+ const unitsA = sortedA.units[accession];
61
+ const unitsB = sortedB.units[accession];
62
+ if (!unitsB)
63
+ continue;
64
+ for (const ua of unitsA) {
65
+ if (ua.size <= bestScore)
66
+ break;
67
+ for (const ub of unitsB) {
68
+ if (ub.size <= bestScore || ua.size <= bestScore)
69
+ break;
70
+ const score = Math.min(ua.size, ub.size);
71
+ if (score > bestScore) {
72
+ bestScore = score;
73
+ bestMatch = { accession, unitA: ua.unitId, unitB: ub.unitId, elementsA: ua.elements, elementsB: ub.elements, nMatchedElements: score };
74
+ }
75
+ }
76
+ }
77
+ }
78
+ return bestMatch;
79
+ }
80
+ const reProtein = /(polypeptide|cyclic-pseudo-peptide)/i;
81
+ function alignAndSuperpose(lociA, lociB) {
82
+ const location = structure_1.StructureElement.Loci.getFirstLocation(lociA);
83
+ const subtype = structure_1.StructureProperties.entity.subtype(location);
84
+ const substMatrix = subtype.match(reProtein) ? 'blosum62' : 'default';
85
+ const { matchedA, matchedB } = computeAlignment(lociA.elements[0], lociB.elements[0], { substMatrix });
86
+ const n = int_1.OrderedSet.size(matchedA.indices);
87
+ const coordsA = (0, superposition_1.getPositionTable)(structure_1.StructureElement.Loci(lociA.structure, [matchedA]), n);
88
+ const coordsB = (0, superposition_1.getPositionTable)(structure_1.StructureElement.Loci(lociB.structure, [matchedB]), n);
89
+ const superposition = minimize_rmsd_1.MinimizeRmsd.compute({ a: coordsA, b: coordsB });
90
+ return Object.assign(Object.assign({}, superposition), { nAlignedElements: n });
91
+ // TODO remove explicit nAlignedElements, once in core Molstar
92
+ }
93
+ function computeAlignment(a, b, options = {}) {
94
+ const seqA = getSequenceFromLoci(a);
95
+ const seqB = getSequenceFromLoci(b);
96
+ const { aliA, aliB, score } = (0, alignment_1.align)(seqA.sequence.map(getOneLetterCode), seqB.sequence.map(getOneLetterCode), options);
97
+ const indicesA = [];
98
+ const indicesB = [];
99
+ let seqIdxA = 0, seqIdxB = 0;
100
+ for (let i = 0, n = aliA.length; i < n; ++i) {
101
+ if (aliA[i] !== '-' && aliB[i] !== '-') {
102
+ indicesA.push(seqA.unitElements[seqIdxA]);
103
+ indicesB.push(seqB.unitElements[seqIdxB]);
104
+ }
105
+ if (aliA[i] !== '-')
106
+ seqIdxA += 1;
107
+ if (aliB[i] !== '-')
108
+ seqIdxB += 1;
109
+ }
110
+ return {
111
+ matchedA: { unit: a.unit, indices: int_1.OrderedSet.ofSortedArray(indicesA) },
112
+ matchedB: { unit: b.unit, indices: int_1.OrderedSet.ofSortedArray(indicesB) },
113
+ score,
114
+ };
115
+ }
116
+ /** Extract sequence and array of corresponding trace atoms. */
117
+ function getSequenceFromLoci(loci) {
118
+ const { unit, indices } = loci;
119
+ const unitElements = [];
120
+ const sequence = [];
121
+ int_1.OrderedSet.forEach(indices, elementUnitIndex => {
122
+ const elementIndex = unit.elements[elementUnitIndex];
123
+ if (int_1.OrderedSet.has(unit.polymerElements, elementIndex)) {
124
+ unitElements.push(elementUnitIndex);
125
+ const compId = unit.model.atomicHierarchy.atoms.label_comp_id.value(elementIndex);
126
+ sequence.push(compId);
127
+ }
128
+ });
129
+ return { sequence, unitElements };
130
+ }
131
+ function getOneLetterCode(compId) {
132
+ var _a;
133
+ return (_a = OneLetterCodes[compId]) !== null && _a !== void 0 ? _a : 'X';
134
+ }
135
+ // Copied from Molstar
136
+ const OneLetterCodes = {
137
+ 'HIS': 'H',
138
+ 'ARG': 'R',
139
+ 'LYS': 'K',
140
+ 'ILE': 'I',
141
+ 'PHE': 'F',
142
+ 'LEU': 'L',
143
+ 'TRP': 'W',
144
+ 'ALA': 'A',
145
+ 'MET': 'M',
146
+ 'PRO': 'P',
147
+ 'CYS': 'C',
148
+ 'ASN': 'N',
149
+ 'VAL': 'V',
150
+ 'GLY': 'G',
151
+ 'SER': 'S',
152
+ 'GLN': 'Q',
153
+ 'TYR': 'Y',
154
+ 'ASP': 'D',
155
+ 'GLU': 'E',
156
+ 'THR': 'T',
157
+ 'SEC': 'U', // as per IUPAC definition
158
+ 'PYL': 'O', // as per IUPAC definition
159
+ // charmm ff
160
+ 'HSD': 'H', 'HSE': 'H', 'HSP': 'H',
161
+ 'LSN': 'K',
162
+ 'ASPP': 'D',
163
+ 'GLUP': 'E',
164
+ // amber ff
165
+ 'HID': 'H', 'HIE': 'H', 'HIP': 'H',
166
+ 'LYN': 'K',
167
+ 'ASH': 'D',
168
+ 'GLH': 'E',
169
+ // DNA
170
+ 'DA': 'A',
171
+ 'DC': 'C',
172
+ 'DG': 'G',
173
+ 'DT': 'T',
174
+ 'DU': 'U',
175
+ // RNA
176
+ 'A': 'A',
177
+ 'C': 'C',
178
+ 'G': 'G',
179
+ 'T': 'T',
180
+ 'U': 'U',
181
+ };
package/lib/helpers.d.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import { ComponentExpressionT } from 'molstar/lib/extensions/mvs/tree/mvs/param-types';
2
+ import { Mat3, Mat4 } from 'molstar/lib/mol-math/linear-algebra';
2
3
  import { Model, Structure } from 'molstar/lib/mol-model/structure';
3
4
  import { BuiltInTrajectoryFormat } from 'molstar/lib/mol-plugin-state/formats/trajectory';
4
5
  import { StructureRef } from 'molstar/lib/mol-plugin-state/manager/structure/hierarchy-state';
6
+ import { InitVolumeStreaming } from 'molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/transformers';
5
7
  import { PluginConfigItem } from 'molstar/lib/mol-plugin/config';
6
8
  import { PluginContext } from 'molstar/lib/mol-plugin/context';
9
+ import { PluginLayoutStateProps } from 'molstar/lib/mol-plugin/layout';
7
10
  import { Expression } from 'molstar/lib/mol-script/language/expression';
8
11
  import { Overpaint } from 'molstar/lib/mol-theme/overpaint';
9
12
  import { Color } from 'molstar/lib/mol-util/color';
@@ -20,9 +23,15 @@ export interface LoadParams {
20
23
  id?: string;
21
24
  }
22
25
  export interface MapParams {
26
+ /** Volume streaming view type (`'off' | 'box' | 'selection-box' | 'camera-target' | 'cell' | 'auto'`) */
27
+ defaultView?: PDBeVolumes.InitVolumeStreamingProps['defaultView'];
28
+ /** Style for EM map */
23
29
  'em'?: MapStyle;
30
+ /** Style for X-ray 2Fo-Fc map */
24
31
  '2fo-fc'?: MapStyle;
32
+ /** Style for X-ray Fo-Fc(+ve) map */
25
33
  'fo-fc(+ve)'?: MapStyle;
34
+ /** Style for X-ray Fo-Fc(-ve) map */
26
35
  'fo-fc(-ve)'?: MapStyle;
27
36
  }
28
37
  interface MapStyle {
@@ -30,7 +39,8 @@ interface MapStyle {
30
39
  wireframe?: boolean;
31
40
  }
32
41
  export declare namespace PDBeVolumes {
33
- function mapParams(defaultParams: any, mapParams?: MapParams, ref?: string | number): any;
42
+ type InitVolumeStreamingProps = ReturnType<(typeof InitVolumeStreaming)['createDefaultParams']>;
43
+ function mapParams(defaultParams: InitVolumeStreamingProps, mapParams?: MapParams): InitVolumeStreamingProps;
34
44
  function displayUsibilityMessage(plugin: PluginContext): void;
35
45
  function toggle(plugin: PluginContext): void;
36
46
  }
@@ -86,6 +96,8 @@ export interface QueryParam {
86
96
  uniprot_residue_number?: number;
87
97
  start_uniprot_residue_number?: number;
88
98
  end_uniprot_residue_number?: number;
99
+ /** List of element type symbols (e.g. ['C', 'N', 'FE']) */
100
+ type_symbol?: string[];
89
101
  }
90
102
  export declare function queryParamsToMvsComponentExpressions(params: QueryParam[]): ComponentExpressionT[];
91
103
  export declare namespace QueryHelper {
@@ -192,4 +204,6 @@ export declare namespace PluginConfigUtils {
192
204
  /** Retrieve config values for items in `configItems` from the current plugin config */
193
205
  function getConfigValues<T extends object>(plugin: PluginContext | undefined, configItems: ConfigFor<T>, defaults: T): T;
194
206
  }
207
+ export declare function pluginLayoutStateFromInitParams(initParams: InitParams): PluginLayoutStateProps;
208
+ export declare function getRotationMat4(view: 'front' | 'back' | 'right' | 'left' | 'top' | 'bottom' | Mat3): Mat4;
195
209
  export {};