pdbe-molstar 3.6.0 → 3.7.1

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 +15 -0
  8. package/lib/extensions/complexes/coloring.js +20 -9
  9. package/lib/extensions/complexes/index.d.ts +32 -17
  10. package/lib/extensions/complexes/index.js +24 -48
  11. package/lib/extensions/complexes/superpose-by-biggest-chain.d.ts +13 -15
  12. package/lib/extensions/complexes/superpose-by-biggest-chain.js +62 -56
  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 +11 -1
  16. package/lib/helpers.js +11 -5
  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/styles/pdbe-molstar/_index.scss +39 -1
  25. package/lib/superposition.d.ts +2 -2
  26. package/lib/superposition.js +41 -60
  27. package/lib/ui/_layout.d.ts +15 -0
  28. package/lib/ui/_layout.js +162 -0
  29. package/lib/ui/alphafold-superposition.js +2 -2
  30. package/lib/ui/layout-no-controls-unless-expanded.d.ts +5 -0
  31. package/lib/ui/layout-no-controls-unless-expanded.js +34 -0
  32. package/lib/ui/pdbe-viewport-controls.d.ts +0 -1
  33. package/lib/ui/pdbe-viewport-controls.js +3 -19
  34. package/lib/ui/split-ui/components.d.ts +3 -0
  35. package/lib/ui/split-ui/components.js +3 -0
  36. package/lib/viewer.d.ts +1 -0
  37. package/lib/viewer.js +3 -3
  38. package/package.json +1 -1
  39. package/lib/superposition-sifts-mapping.d.ts +0 -22
  40. package/lib/superposition-sifts-mapping.js +0 -153
@@ -3,9 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.Coloring = void 0;
5
5
  exports.loadComplexSuperposition = loadComplexSuperposition;
6
- exports.superposeStructuresByMolstarDefault = superposeStructuresByMolstarDefault;
7
6
  const tslib_1 = require("tslib");
8
- const superposition_sifts_mapping_1 = require("molstar/lib/mol-model/structure/structure/util/superposition-sifts-mapping");
9
7
  const commands_1 = require("molstar/lib/mol-plugin/commands");
10
8
  const sleep_1 = require("molstar/lib/mol-util/sleep");
11
9
  const __1 = require("../..");
@@ -13,12 +11,26 @@ const helpers_1 = require("../../helpers");
13
11
  const superposition_1 = require("../../superposition");
14
12
  const Coloring = tslib_1.__importStar(require("./coloring"));
15
13
  const superpose_by_biggest_chain_1 = require("./superpose-by-biggest-chain");
14
+ const superpose_by_sequence_alignment_1 = require("./superpose-by-sequence-alignment");
16
15
  exports.Coloring = tslib_1.__importStar(require("./coloring"));
17
- /** Load a structure, superpose onto the main structure based on Uniprot residue numbers, and optionally apply coloring to show common/additional components. */
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
+ */
18
30
  function loadComplexSuperposition(viewer, params) {
19
31
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
20
32
  var _a, _b, _c, _d, _e, _f, _g;
21
- const { pdbId, assemblyId, animationDuration = 250, coloring, baseComponents, otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors, method = 'biggest-matched-chain' } = params;
33
+ const { pdbId, assemblyId, animationDuration = 250, coloring, baseComponents, otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors } = params;
22
34
  const baseStructId = __1.PDBeMolstarPlugin.MAIN_STRUCTURE_ID;
23
35
  const otherStructId = (_a = params.id) !== null && _a !== void 0 ? _a : `${pdbId}_${assemblyId}`;
24
36
  // Apply coloring to base structure
@@ -45,12 +57,17 @@ function loadComplexSuperposition(viewer, params) {
45
57
  if (!otherStruct)
46
58
  throw new Error('Mobile structure not loaded');
47
59
  // Superpose other structure on base structure
48
- const { status, superposition } = method === 'biggest-matched-chain' ?
49
- (0, superpose_by_biggest_chain_1.superposeStructuresByBiggestCommonChain)(baseStruct, otherStruct, baseComponents, otherComponents)
50
- : superposeStructuresByMolstarDefault(baseStruct, otherStruct);
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
+ }
51
65
  if (superposition) {
52
66
  yield (0, superposition_1.transform)(viewer.plugin, viewer.getStructure(otherStructId).cell, superposition.bTransform);
53
67
  }
68
+ else {
69
+ console.log(`Sequence alignment superposition method failed, leaving unsuperposed`);
70
+ }
54
71
  // Apply coloring to other structure
55
72
  if (coloring === 'subcomplex') {
56
73
  yield Coloring.colorSubcomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors });
@@ -74,50 +91,9 @@ function loadComplexSuperposition(viewer, params) {
74
91
  // Reveal new structure
75
92
  yield viewer.visual.structureVisibility(otherStructId, true);
76
93
  return {
77
- /** Structure identifier of the newly loaded complex structure, to refer to this structure later */
78
94
  id: otherStructId,
79
- /** Status of pairwise superposition ('success' / 'zero-overlap' / 'failed') */
80
- status,
81
- /** Superposition RMSD and tranform (if status is 'success') */
82
95
  superposition,
83
- /** Function that deletes the newly loaded complex structure */
84
96
  delete: () => viewer.deleteStructure(otherStructId),
85
97
  };
86
98
  });
87
99
  }
88
- /** Superpose mobile structure onto static structure, based on Uniprot residue numbers. */
89
- function superposeStructuresByMolstarDefault(staticStruct, mobileStruct) {
90
- var _a;
91
- const aln = (0, superposition_sifts_mapping_1.alignAndSuperposeWithSIFTSMapping)([staticStruct, mobileStruct], { traceOnly: true });
92
- const superposition = (_a = aln.entries.find(e => e.pivot === 0 && e.other === 1)) === null || _a === void 0 ? void 0 : _a.transform;
93
- if (superposition) {
94
- return { status: 'success', superposition: Object.assign(Object.assign({}, superposition), { nAlignedElements: Number.NaN }) };
95
- }
96
- else if (aln.zeroOverlapPairs.find(e => e[0] === 0 && e[1] === 1)) {
97
- return { status: 'zero-overlap', superposition: undefined };
98
- }
99
- else {
100
- return { status: 'failed', superposition: undefined };
101
- }
102
- }
103
- /*
104
- Coloring - subcomplexes:
105
- - base common -> by entity, lighter
106
- - base additional -> gray, lighter
107
- - sub common -> by entity, darker
108
- - sub additional -> gray, darker (these are all unmapped components, includes antibodies and ligands)
109
-
110
- -> Colors can be assigned based on base complex and applied to subcomplex
111
-
112
- Coloring - supercomplexes:
113
- - base common -> gray, lighter
114
- - base additional -> unmapped color, lighter (these are all unmapped components, includes antibodies and ligands)
115
- - super common -> gray, darker
116
- - super additional mapped -> by entity, darker
117
- - super additional unmapped -> unmapped color, darker
118
-
119
- -> Colors can be assigned based on supercomplex complex, consistency between supercomplexes is probably not necessary
120
-
121
- -> For both subcomplexes and supercomplexes, colors could be assigned based on UniprotID hash -> database-wide consistency but complexes with similar-color components will occur
122
-
123
- */
@@ -1,17 +1,15 @@
1
- import { MinimizeRmsd } from 'molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd';
2
1
  import { Structure } from 'molstar/lib/mol-model/structure';
3
- export type SuperpositionResult = {
4
- status: 'success';
5
- superposition: MinimizeRmsd.Result & {
6
- nAlignedElements: number;
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)[];
7
14
  };
8
- } | {
9
- status: 'zero-overlap';
10
- superposition: undefined;
11
- } | {
12
- status: 'failed';
13
- superposition: undefined;
14
- };
15
- /** Status of pairwise superposition (success = superposed, zero-overlap = failed to superpose because the two structures have no matchable elements, failed = failed to superpose for other reasons) */
16
- export type SuperpositionStatus = SuperpositionResult['status'];
17
- export declare function superposeStructuresByBiggestCommonChain(structA: Structure, structB: Structure, allowedComponentsA: string[] | undefined, allowedComponentsB: string[] | undefined): SuperpositionResult;
15
+ }
@@ -1,8 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.superposeStructuresByBiggestCommonChain = superposeStructuresByBiggestCommonChain;
3
+ exports.superposeByBiggestCommonChain = superposeByBiggestCommonChain;
4
4
  const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd");
5
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
+ }
6
45
  function extractUniprotIndex(structure, allowedAccessions) {
7
46
  var _a, _b, _c;
8
47
  var _d, _e;
@@ -42,9 +81,29 @@ function extractUniprotIndex(structure, allowedAccessions) {
42
81
  }
43
82
  return out;
44
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
+ }
45
104
  function bestUniprotMatch(a, b) {
46
- const sortedA = sortAccessionAndChains(a);
47
- const sortedB = sortAccessionAndChains(b);
105
+ const sortedA = sortAccessionsAndUnits(a);
106
+ const sortedB = sortAccessionsAndUnits(b);
48
107
  let bestMatch = undefined;
49
108
  let bestScore = 0;
50
109
  for (const accession of sortedA.accessions) {
@@ -70,26 +129,6 @@ function bestUniprotMatch(a, b) {
70
129
  }
71
130
  return bestMatch;
72
131
  }
73
- /** Sort units for each accession by decreasing size and sort accessions by decreasing biggest unit size. */
74
- function sortAccessionAndChains(uniprotIndex) {
75
- const unitsByAccession = {};
76
- for (const accession in uniprotIndex) {
77
- const unitIds = uniprotIndex[accession];
78
- const units = [];
79
- for (const unitId in unitIds) {
80
- const size = Object.keys(unitIds[unitId].atomMap).length;
81
- units.push({ unitId, size });
82
- }
83
- units.sort((a, b) => b.size - a.size);
84
- unitsByAccession[accession] = units;
85
- }
86
- return {
87
- /** Accessions sorted by decreasing biggest unit size */
88
- accessions: Object.keys(unitsByAccession).sort((a, b) => unitsByAccession[b][0].size - unitsByAccession[a][0].size),
89
- /** Units per accession, sorted by decreasing unit size */
90
- units: unitsByAccession,
91
- };
92
- }
93
132
  /** Return number of keys common to objects `a` and `b` */
94
133
  function objectKeyOverlap(a, b) {
95
134
  let overlap = 0;
@@ -100,36 +139,3 @@ function objectKeyOverlap(a, b) {
100
139
  }
101
140
  return overlap;
102
141
  }
103
- function superposeStructuresByBiggestCommonChain(structA, structB, allowedComponentsA, allowedComponentsB) {
104
- const indexA = extractUniprotIndex(structA, allowedComponentsA);
105
- const indexB = extractUniprotIndex(structB, allowedComponentsB);
106
- const bestMatch = bestUniprotMatch(indexA, indexB);
107
- if (!bestMatch) {
108
- return { status: 'zero-overlap', superposition: undefined };
109
- }
110
- const unitA = structA.unitMap.get(Number(bestMatch.unitA));
111
- const unitB = structB.unitMap.get(Number(bestMatch.unitB));
112
- const unitIndexA = indexA[bestMatch.accession][bestMatch.unitA];
113
- const unitIndexB = indexB[bestMatch.accession][bestMatch.unitB];
114
- const positionsA = minimize_rmsd_1.MinimizeRmsd.Positions.empty(bestMatch.nMatchedElements);
115
- const positionsB = minimize_rmsd_1.MinimizeRmsd.Positions.empty(bestMatch.nMatchedElements);
116
- let i = 0;
117
- for (const unpNum in unitIndexA.atomMap) {
118
- const iAtomB = unitIndexB.atomMap[unpNum];
119
- if (iAtomB === undefined)
120
- continue;
121
- const iAtomA = unitIndexA.atomMap[unpNum];
122
- positionsA.x[i] = unitA.conformation.coordinates.x[iAtomA];
123
- positionsA.y[i] = unitA.conformation.coordinates.y[iAtomA];
124
- positionsA.z[i] = unitA.conformation.coordinates.z[iAtomA];
125
- positionsB.x[i] = unitB.conformation.coordinates.x[iAtomB];
126
- positionsB.y[i] = unitB.conformation.coordinates.y[iAtomB];
127
- positionsB.z[i] = unitB.conformation.coordinates.z[iAtomB];
128
- i++;
129
- }
130
- const superposition = minimize_rmsd_1.MinimizeRmsd.compute({ a: positionsA, b: positionsB });
131
- if (isNaN(superposition.rmsd)) {
132
- return { status: 'failed', superposition: undefined };
133
- }
134
- return { status: 'success', superposition: Object.assign(Object.assign({}, superposition), { nAlignedElements: bestMatch.nMatchedElements }) };
135
- }
@@ -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
@@ -3,6 +3,7 @@ import { Mat3, Mat4 } from 'molstar/lib/mol-math/linear-algebra';
3
3
  import { Model, Structure } from 'molstar/lib/mol-model/structure';
4
4
  import { BuiltInTrajectoryFormat } from 'molstar/lib/mol-plugin-state/formats/trajectory';
5
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';
6
7
  import { PluginConfigItem } from 'molstar/lib/mol-plugin/config';
7
8
  import { PluginContext } from 'molstar/lib/mol-plugin/context';
8
9
  import { PluginLayoutStateProps } from 'molstar/lib/mol-plugin/layout';
@@ -22,9 +23,15 @@ export interface LoadParams {
22
23
  id?: string;
23
24
  }
24
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 */
25
29
  'em'?: MapStyle;
30
+ /** Style for X-ray 2Fo-Fc map */
26
31
  '2fo-fc'?: MapStyle;
32
+ /** Style for X-ray Fo-Fc(+ve) map */
27
33
  'fo-fc(+ve)'?: MapStyle;
34
+ /** Style for X-ray Fo-Fc(-ve) map */
28
35
  'fo-fc(-ve)'?: MapStyle;
29
36
  }
30
37
  interface MapStyle {
@@ -32,7 +39,8 @@ interface MapStyle {
32
39
  wireframe?: boolean;
33
40
  }
34
41
  export declare namespace PDBeVolumes {
35
- 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;
36
44
  function displayUsibilityMessage(plugin: PluginContext): void;
37
45
  function toggle(plugin: PluginContext): void;
38
46
  }
@@ -88,6 +96,8 @@ export interface QueryParam {
88
96
  uniprot_residue_number?: number;
89
97
  start_uniprot_residue_number?: number;
90
98
  end_uniprot_residue_number?: number;
99
+ /** List of element type symbols (e.g. ['C', 'N', 'FE']) */
100
+ type_symbol?: string[];
91
101
  }
92
102
  export declare function queryParamsToMvsComponentExpressions(params: QueryParam[]): ComponentExpressionT[];
93
103
  export declare namespace QueryHelper {
package/lib/helpers.js CHANGED
@@ -34,9 +34,12 @@ const sleep_1 = require("molstar/lib/mol-util/sleep");
34
34
  const sifts_mapping_1 = require("./sifts-mapping");
35
35
  var PDBeVolumes;
36
36
  (function (PDBeVolumes) {
37
- function mapParams(defaultParams, mapParams, ref) {
37
+ function mapParams(defaultParams, mapParams) {
38
38
  const pdbeParams = Object.assign({}, defaultParams);
39
- pdbeParams.options.behaviorRef = 'volume-streaming' + '' + Math.floor(Math.random() * Math.floor(100));
39
+ if (mapParams === null || mapParams === void 0 ? void 0 : mapParams.defaultView) {
40
+ pdbeParams.defaultView = mapParams.defaultView;
41
+ }
42
+ pdbeParams.options.behaviorRef = `volume-streaming${Math.floor(Math.random() * 100)}`;
40
43
  pdbeParams.options.emContourProvider = 'pdbe';
41
44
  pdbeParams.options.serverUrl = 'https://www.ebi.ac.uk/pdbe/volume-server';
42
45
  const MAIN_MAP_DEFAULTS = { opacity: 0.49, wireframe: false };
@@ -152,7 +155,7 @@ var LigandView;
152
155
  LigandView.branchedQuery = branchedQuery;
153
156
  })(LigandView || (exports.LigandView = LigandView = {}));
154
157
  function queryParamsToMvsComponentExpressions(params) {
155
- const broadcasted = broadcast(params, ['atoms', 'atom_id']);
158
+ const broadcasted = broadcast(params, ['atoms', 'atom_id', 'type_symbol']);
156
159
  return broadcasted.map(item => {
157
160
  var _a;
158
161
  return ({
@@ -168,7 +171,7 @@ function queryParamsToMvsComponentExpressions(params) {
168
171
  end_auth_seq_id: item.end_auth_residue_number,
169
172
  label_atom_id: item.atoms,
170
173
  auth_atom_id: undefined,
171
- type_symbol: undefined,
174
+ type_symbol: item.type_symbol,
172
175
  atom_id: item.atom_id,
173
176
  atom_index: undefined,
174
177
  label_comp_id: item.label_comp_id,
@@ -282,6 +285,9 @@ var QueryHelper;
282
285
  if (param.atom_id) {
283
286
  predicates.atom.push(l => param.atom_id.includes(structure_1.StructureProperties.atom.id(l.element)));
284
287
  }
288
+ if (param.type_symbol) {
289
+ predicates.atom.push(l => param.type_symbol.includes(structure_1.StructureProperties.atom.type_symbol(l.element)));
290
+ }
285
291
  selections.push({
286
292
  entityTest: predicateConjunction(predicates.entity),
287
293
  chainTest: predicateConjunction(predicates.chain),
@@ -289,7 +295,7 @@ var QueryHelper;
289
295
  atomTest: predicateConjunction(predicates.atom),
290
296
  });
291
297
  }
292
- const atmGroupsQueries = selections.map(selection => structure_1.Queries.generators.atoms(selection));
298
+ const atmGroupsQueries = selections.map(selection => structure_1.Queries.modifiers.union(structure_1.Queries.generators.atoms(selection)));
293
299
  return structure_1.Queries.combinators.merge(atmGroupsQueries);
294
300
  }
295
301
  QueryHelper.getQueryObject = getQueryObject;
@@ -1,4 +1,3 @@
1
- import { SymmetryOperator } from 'molstar/lib/mol-math/geometry';
2
1
  import { Mat4 } from 'molstar/lib/mol-math/linear-algebra';
3
2
  import { PluginContext } from 'molstar/lib/mol-plugin/context';
4
3
  import { StateSelection, StateTransform } from 'molstar/lib/mol-state';
@@ -55,7 +54,6 @@ export interface PluginCustomState {
55
54
  visibility: boolean[];
56
55
  transforms: Mat4[];
57
56
  rmsds: string[][];
58
- coordinateSystems: (SymmetryOperator | undefined)[];
59
57
  };
60
58
  };
61
59
  superpositionError?: string;
@@ -0,0 +1,5 @@
1
+ import { PluginBehavior } from 'molstar/lib/mol-plugin/behavior';
2
+ /** Allows coloring residues in sequence panel */
3
+ export declare const SequenceColor: import("molstar/lib/mol-state").StateTransformer<PluginBehavior.Category, PluginBehavior.Behavior, {
4
+ autoAttach: boolean;
5
+ }>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SequenceColor = void 0;
4
+ const model_1 = require("molstar/lib/mol-plugin-state/transforms/model");
5
+ const context_1 = require("molstar/lib/mol-plugin-ui/context");
6
+ const behavior_1 = require("molstar/lib/mol-plugin/behavior");
7
+ const param_definition_1 = require("molstar/lib/mol-util/param-definition");
8
+ const rxjs_1 = require("rxjs");
9
+ const color_1 = require("./color");
10
+ const prop_1 = require("./prop");
11
+ /** Allows coloring residues in sequence panel */
12
+ exports.SequenceColor = behavior_1.PluginBehavior.create({
13
+ name: 'sequence-color',
14
+ category: 'misc',
15
+ display: {
16
+ name: 'Sequence Color',
17
+ description: 'Sequence Color extension, allows assigning custom residue colors to be shown in the sequence panel, based on a custom structure property',
18
+ },
19
+ ctor: class extends behavior_1.PluginBehavior.Handler {
20
+ register() {
21
+ var _a, _b;
22
+ this.ctx.customStructureProperties.register(prop_1.SequenceColorProperty.Provider, this.params.autoAttach);
23
+ if (this.ctx instanceof context_1.PluginUIContext) {
24
+ const customUIState = (_a = this.ctx.customUIState) !== null && _a !== void 0 ? _a : {}; // TODO remove this hack once `customUIState` is available in core Molstar
25
+ const theme = (_b = customUIState.experimentalSequenceColorTheme) !== null && _b !== void 0 ? _b : (customUIState.experimentalSequenceColorTheme = new rxjs_1.BehaviorSubject(undefined));
26
+ this.sub = this.ctx.state.events.cell.stateUpdated.subscribe(s => {
27
+ if (s.cell.transform.transformer === model_1.CustomStructureProperties) {
28
+ theme.next(color_1.CustomSequenceColorTheme.Provider);
29
+ }
30
+ });
31
+ }
32
+ }
33
+ update(p) {
34
+ const updated = this.params.autoAttach !== p.autoAttach;
35
+ this.params.autoAttach = p.autoAttach;
36
+ this.ctx.customStructureProperties.setDefaultAutoAttach(prop_1.SequenceColorProperty.Provider.descriptor.name, this.params.autoAttach);
37
+ return updated;
38
+ }
39
+ unregister() {
40
+ var _a, _b;
41
+ this.ctx.customStructureProperties.unregister(prop_1.SequenceColorProperty.Provider.descriptor.name);
42
+ (_a = this.sub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
43
+ this.sub = undefined;
44
+ if (this.ctx instanceof context_1.PluginUIContext) {
45
+ const customUIState = (_b = this.ctx.customUIState) !== null && _b !== void 0 ? _b : {}; // TODO remove this hack once `customUIState` is available in core Molstar
46
+ const theme = customUIState.experimentalSequenceColorTheme;
47
+ theme === null || theme === void 0 ? void 0 : theme.next(undefined);
48
+ }
49
+ }
50
+ },
51
+ params: () => ({
52
+ autoAttach: param_definition_1.ParamDefinition.Boolean(true),
53
+ }),
54
+ });