pdbe-molstar 3.4.0 → 3.6.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 (73) 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 +26 -1
  5. package/build/pdbe-molstar.css +2 -2
  6. package/lib/alphafold-transparency.js +5 -6
  7. package/lib/custom-events.js +1 -1
  8. package/lib/domain-annotations/behavior.d.ts +1 -1
  9. package/lib/domain-annotations/color.js +2 -2
  10. package/lib/domain-annotations/prop.js +1 -2
  11. package/lib/extensions/complexes/coloring.d.ts +40 -0
  12. package/lib/extensions/complexes/coloring.js +118 -0
  13. package/lib/extensions/complexes/index.d.ts +55 -0
  14. package/lib/extensions/complexes/index.js +123 -0
  15. package/lib/extensions/complexes/superpose-by-biggest-chain.d.ts +17 -0
  16. package/lib/extensions/complexes/superpose-by-biggest-chain.js +135 -0
  17. package/lib/extensions/foldseek/index.d.ts +1 -1
  18. package/lib/extensions/foldseek/index.js +7 -7
  19. package/lib/extensions/interactions/index.d.ts +36 -0
  20. package/lib/extensions/interactions/index.js +112 -0
  21. package/lib/extensions/state-gallery/behavior.d.ts +1 -1
  22. package/lib/extensions/state-gallery/config.js +2 -2
  23. package/lib/extensions/state-gallery/manager.js +5 -4
  24. package/lib/extensions/state-gallery/ui.js +12 -12
  25. package/lib/helpers.d.ts +8 -0
  26. package/lib/helpers.js +175 -81
  27. package/lib/labels.d.ts +1 -1
  28. package/lib/loci-details.js +4 -5
  29. package/lib/plugin-custom-state.js +2 -2
  30. package/lib/sifts-mapping.js +1 -2
  31. package/lib/sifts-mappings-behaviour.d.ts +1 -1
  32. package/lib/spec-from-html.js +1 -2
  33. package/lib/spec.d.ts +1 -1
  34. package/lib/spec.js +3 -3
  35. package/lib/styles/pdbe-molstar/_index.scss +7 -7
  36. package/lib/styles/pdbe-molstar-dark.scss +2 -2
  37. package/lib/styles/pdbe-molstar-light.scss +2 -2
  38. package/lib/subscribe-events.js +1 -2
  39. package/lib/superposition-export.js +1 -2
  40. package/lib/superposition-focus-representation.d.ts +2 -2
  41. package/lib/superposition-focus-representation.js +1 -1
  42. package/lib/superposition-sifts-mapping.js +1 -2
  43. package/lib/superposition.d.ts +1 -1
  44. package/lib/superposition.js +34 -39
  45. package/lib/ui/alphafold-superposition.js +6 -6
  46. package/lib/ui/annotation-controls.d.ts +1 -1
  47. package/lib/ui/annotation-controls.js +2 -2
  48. package/lib/ui/annotation-row-controls.js +3 -3
  49. package/lib/ui/custom-controls.js +1 -1
  50. package/lib/ui/export-superposition.d.ts +0 -1
  51. package/lib/ui/export-superposition.js +1 -1
  52. package/lib/ui/icons.js +1 -1
  53. package/lib/ui/left-panel/core.d.ts +0 -1
  54. package/lib/ui/left-panel/core.js +5 -6
  55. package/lib/ui/left-panel/pdbe-left-panel.d.ts +0 -1
  56. package/lib/ui/left-panel/tabs.d.ts +1 -1
  57. package/lib/ui/left-panel/tabs.js +2 -2
  58. package/lib/ui/overlay.js +3 -4
  59. package/lib/ui/pdbe-screenshot-controls.js +4 -4
  60. package/lib/ui/pdbe-structure-controls.js +3 -3
  61. package/lib/ui/pdbe-viewport-controls.js +3 -4
  62. package/lib/ui/pdbe-viewport.d.ts +5 -2
  63. package/lib/ui/pdbe-viewport.js +24 -3
  64. package/lib/ui/segment-tree.js +40 -45
  65. package/lib/ui/split-ui/components.d.ts +1 -2
  66. package/lib/ui/split-ui/components.js +2 -2
  67. package/lib/ui/split-ui/split-ui.js +6 -7
  68. package/lib/ui/superposition-components.js +16 -21
  69. package/lib/ui/symmetry-annotation-controls.d.ts +4 -4
  70. package/lib/ui/symmetry-annotation-controls.js +18 -25
  71. package/lib/viewer.d.ts +31 -3
  72. package/lib/viewer.js +54 -40
  73. package/package.json +4 -4
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.colorComponents = colorComponents;
4
+ exports.colorSubcomplex = colorSubcomplex;
5
+ exports.colorSupercomplex = colorSupercomplex;
6
+ const tslib_1 = require("tslib");
7
+ const color_1 = require("molstar/lib/mol-util/color");
8
+ const helpers_1 = require("../../helpers");
9
+ const DEFAULT_CORE_COLOR = '#d8d8d8';
10
+ const DEFAULT_UNMAPPED_COLOR = '#222222';
11
+ const DEFAULT_COMPONENT_COLORS = [
12
+ '#1b9e77', '#d95f02', '#7570b3', '#e7298a', '#66a61e', '#e6ab02', '#a6761d', // Dark-2
13
+ '#7f3c8d', '#11a579', '#3969ac', '#f2b701', '#e73f74', '#80ba5a', '#e68310', '#008695', '#cf1c90', '#f97b72', // Bold
14
+ '#66c5cc', '#f6cf71', '#f89c74', '#dcb0f2', '#87c55f', '#9eb9f3', '#fe88b1', '#c9db74', '#8be0a4', '#b497e7', // Pastel
15
+ '#e5c494', '#66c2a5', '#fc8d62', '#8da0cb', '#e78ac3', '#a6d854', // Set-2
16
+ ];
17
+ // TODO compare color variants:
18
+ // http://127.0.0.1:1339/complexes-demo.html?complexId=PDB-CPX-159519&unmappedColor=%23222222
19
+ // http://127.0.0.1:1339/complexes-demo.html?complexId=PDB-CPX-159519&unmappedColor=%23ff0000
20
+ /** How much lighter/darker colors should be for the base/other complex */
21
+ const COLOR_ADJUSTMENT_STRENGTH = 0.75;
22
+ function colorComponents(viewer, params) {
23
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
24
+ const { coreColor = DEFAULT_CORE_COLOR, componentColors = DEFAULT_COMPONENT_COLORS, components, mappings = {} } = params;
25
+ const colorData = [];
26
+ const tooltipData = [{ tooltip: '<b>Base complex</b>' }];
27
+ for (let i = 0; i < components.length; i++) {
28
+ const accession = components[i];
29
+ const color = componentColors[i % componentColors.length];
30
+ colorData.push(...selectorItems(accession, mappings[accession], { color: adjustForBase(color) }));
31
+ tooltipData.push(...selectorItems(accession, mappings[accession], { tooltip: `<b>component ${accession}</b>` }));
32
+ }
33
+ yield viewer.visual.select({ data: colorData, nonSelectedColor: adjustForBase(coreColor), structureId: params.structId });
34
+ yield viewer.visual.tooltips({ data: tooltipData, structureId: params.structId });
35
+ });
36
+ }
37
+ function colorSubcomplex(viewer, params) {
38
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
39
+ const { coreColor = DEFAULT_CORE_COLOR, componentColors = DEFAULT_COMPONENT_COLORS, baseComponents, baseMappings = {}, otherMappings = {} } = params;
40
+ const subComponentsSet = new Set(params.otherComponents);
41
+ const baseColorData = [];
42
+ const baseTooltipData = [{ tooltip: '<b>Base complex</b>' }];
43
+ const subColorData = [];
44
+ const subTooltipData = [{ tooltip: '<b>Subcomplex</b>' }];
45
+ for (let i = 0; i < baseComponents.length; i++) {
46
+ const accession = baseComponents[i];
47
+ if (subComponentsSet.has(accession)) {
48
+ const color = componentColors[i % componentColors.length];
49
+ baseColorData.push(...selectorItems(accession, baseMappings[accession], { color: adjustForBase(color) }));
50
+ baseTooltipData.push(...selectorItems(accession, baseMappings[accession], { tooltip: `<b>common component ${accession}</b>` }));
51
+ subColorData.push(...selectorItems(accession, otherMappings[accession], { color: adjustForOther(color) }));
52
+ subTooltipData.push(...selectorItems(accession, otherMappings[accession], { tooltip: `<b>common component ${accession}</b>` }));
53
+ }
54
+ else {
55
+ baseTooltipData.push(...selectorItems(accession, baseMappings[accession], { tooltip: `<b>additional component ${accession}</b>` }));
56
+ }
57
+ }
58
+ if (params.baseStructId) {
59
+ yield viewer.visual.select({ data: baseColorData, nonSelectedColor: adjustForBase(coreColor), structureId: params.baseStructId });
60
+ yield viewer.visual.tooltips({ data: baseTooltipData, structureId: params.baseStructId });
61
+ }
62
+ if (params.otherStructId) {
63
+ yield viewer.visual.select({ data: subColorData, nonSelectedColor: adjustForOther(coreColor), structureId: params.otherStructId });
64
+ yield viewer.visual.tooltips({ data: subTooltipData, structureId: params.otherStructId });
65
+ }
66
+ });
67
+ }
68
+ function colorSupercomplex(viewer, params) {
69
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
70
+ const { coreColor = DEFAULT_CORE_COLOR, unmappedColor = DEFAULT_UNMAPPED_COLOR, componentColors = DEFAULT_COMPONENT_COLORS, baseMappings = {}, otherMappings = {} } = params;
71
+ const baseComponentsSet = new Set(params.baseComponents);
72
+ const superComponents = params.baseComponents.concat(params.otherComponents.filter(acc => !baseComponentsSet.has(acc))); // reorder supercomplex accessions so that colors are consistent with the base
73
+ const baseColorData = [];
74
+ const baseTooltipData = [{ tooltip: '<b>Base complex</b>' }];
75
+ const superColorData = [];
76
+ const superTooltipData = [{ tooltip: '<b>Supercomplex</b>' }];
77
+ for (let i = 0; i < superComponents.length; i++) {
78
+ const accession = superComponents[i];
79
+ if (baseComponentsSet.has(accession)) {
80
+ baseColorData.push(...selectorItems(accession, baseMappings[accession], { color: adjustForBase(coreColor) }));
81
+ baseTooltipData.push(...selectorItems(accession, baseMappings[accession], { tooltip: `<b>common component ${accession}</b>` }));
82
+ superColorData.push(...selectorItems(accession, otherMappings[accession], { color: adjustForOther(coreColor) }));
83
+ superTooltipData.push(...selectorItems(accession, otherMappings[accession], { tooltip: `<b>common component ${accession}</b>` }));
84
+ }
85
+ else {
86
+ const color = componentColors[i % componentColors.length];
87
+ superColorData.push(...selectorItems(accession, otherMappings[accession], { color: adjustForOther(color) }));
88
+ superTooltipData.push(...selectorItems(accession, otherMappings[accession], { tooltip: `<b>additional component ${accession}</b>` }));
89
+ }
90
+ }
91
+ if (params.baseStructId) {
92
+ yield viewer.visual.select({ data: baseColorData, nonSelectedColor: adjustForBase(unmappedColor), structureId: params.baseStructId });
93
+ yield viewer.visual.tooltips({ data: baseTooltipData, structureId: params.baseStructId });
94
+ }
95
+ if (params.otherStructId) {
96
+ yield viewer.visual.select({ data: superColorData, nonSelectedColor: adjustForOther(unmappedColor), structureId: params.otherStructId });
97
+ yield viewer.visual.tooltips({ data: superTooltipData, structureId: params.otherStructId });
98
+ }
99
+ });
100
+ }
101
+ /** Adjust color for use on the base structure (slightly lighten) */
102
+ function adjustForBase(color) {
103
+ return color_1.Color.toHexStyle(color_1.Color.lighten((0, helpers_1.normalizeColor)(color), COLOR_ADJUSTMENT_STRENGTH));
104
+ }
105
+ /** Adjust color for use on the subcomplex/supercomplex structure (slightly darken) */
106
+ function adjustForOther(color) {
107
+ return color_1.Color.toHexStyle(color_1.Color.darken((0, helpers_1.normalizeColor)(color), COLOR_ADJUSTMENT_STRENGTH));
108
+ }
109
+ /** Create items for `.visual.select` or `.visual.tooltip`, preferrably based on `mappings`, or based on `uniprot_accession` if `mappings` not provided. */
110
+ function selectorItems(uniprot_accession, mappings, extras) {
111
+ if (mappings) {
112
+ return mappings.map(m => (Object.assign(Object.assign({}, m), extras)));
113
+ }
114
+ else {
115
+ return [Object.assign({ uniprot_accession }, extras)];
116
+ }
117
+ }
118
+ ;
@@ -0,0 +1,55 @@
1
+ /** Helper functions to allow superposition of complexes */
2
+ import { Structure } from 'molstar/lib/mol-model/structure';
3
+ import { PDBeMolstarPlugin } from '../..';
4
+ import { QueryParam } from '../../helpers';
5
+ import { SuperpositionResult } from './superpose-by-biggest-chain';
6
+ export * as Coloring from './coloring';
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
+ baseMappings?: {
32
+ [accession: string]: QueryParam[];
33
+ };
34
+ /** 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) */
35
+ otherMappings?: {
36
+ [accession: string]: QueryParam[];
37
+ };
38
+ /** Superposition method */
39
+ method?: 'biggest-matched-chain' | 'molstar-builtin';
40
+ }
41
+ /** Load a structure, superpose onto the main structure based on Uniprot residue numbers, and optionally apply coloring to show common/additional components. */
42
+ export declare function loadComplexSuperposition(viewer: PDBeMolstarPlugin, params: LoadComplexSuperpositionParams): Promise<{
43
+ /** Structure identifier of the newly loaded complex structure, to refer to this structure later */
44
+ id: string;
45
+ /** Status of pairwise superposition ('success' / 'zero-overlap' / 'failed') */
46
+ status: "success" | "zero-overlap" | "failed";
47
+ /** Superposition RMSD and tranform (if status is 'success') */
48
+ superposition: (import("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd").MinimizeRmsd.Result & {
49
+ nAlignedElements: number;
50
+ }) | undefined;
51
+ /** Function that deletes the newly loaded complex structure */
52
+ delete: () => Promise<void>;
53
+ }>;
54
+ /** Superpose mobile structure onto static structure, based on Uniprot residue numbers. */
55
+ export declare function superposeStructuresByMolstarDefault(staticStruct: Structure, mobileStruct: Structure): SuperpositionResult;
@@ -0,0 +1,123 @@
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
+ exports.superposeStructuresByMolstarDefault = superposeStructuresByMolstarDefault;
7
+ const tslib_1 = require("tslib");
8
+ const superposition_sifts_mapping_1 = require("molstar/lib/mol-model/structure/structure/util/superposition-sifts-mapping");
9
+ const commands_1 = require("molstar/lib/mol-plugin/commands");
10
+ const sleep_1 = require("molstar/lib/mol-util/sleep");
11
+ const __1 = require("../..");
12
+ const helpers_1 = require("../../helpers");
13
+ const superposition_1 = require("../../superposition");
14
+ const Coloring = tslib_1.__importStar(require("./coloring"));
15
+ const superpose_by_biggest_chain_1 = require("./superpose-by-biggest-chain");
16
+ 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. */
18
+ function loadComplexSuperposition(viewer, params) {
19
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
20
+ 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;
22
+ const baseStructId = __1.PDBeMolstarPlugin.MAIN_STRUCTURE_ID;
23
+ const otherStructId = (_a = params.id) !== null && _a !== void 0 ? _a : `${pdbId}_${assemblyId}`;
24
+ // Apply coloring to base structure
25
+ if (coloring) {
26
+ if (!baseComponents)
27
+ throw new Error('`baseComponents` is required when `coloring` is not `undefined`');
28
+ if (!otherComponents)
29
+ throw new Error('`otherComponents` is required when `coloring` is not `undefined`');
30
+ }
31
+ if (coloring === 'subcomplex') {
32
+ yield Coloring.colorSubcomplex(viewer, { baseStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors });
33
+ }
34
+ if (coloring === 'supercomplex') {
35
+ yield Coloring.colorSupercomplex(viewer, { baseStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors });
36
+ }
37
+ // Load other structure
38
+ const otherStructUrl = (0, helpers_1.getStructureUrl)(viewer.initParams, { pdbId, queryType: 'full' });
39
+ yield viewer.load({ url: otherStructUrl, isBinary: viewer.initParams.encoding === 'bcif', assemblyId, id: otherStructId }, false);
40
+ yield viewer.visual.structureVisibility(otherStructId, false); // hide structure until superposition complete, to avoid flickering
41
+ const baseStruct = (_c = (_b = viewer.getStructure(baseStructId)) === null || _b === void 0 ? void 0 : _b.cell.obj) === null || _c === void 0 ? void 0 : _c.data;
42
+ if (!baseStruct)
43
+ throw new Error('Static structure not loaded');
44
+ const otherStruct = (_e = (_d = viewer.getStructure(otherStructId)) === null || _d === void 0 ? void 0 : _d.cell.obj) === null || _e === void 0 ? void 0 : _e.data;
45
+ if (!otherStruct)
46
+ throw new Error('Mobile structure not loaded');
47
+ // 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);
51
+ if (superposition) {
52
+ yield (0, superposition_1.transform)(viewer.plugin, viewer.getStructure(otherStructId).cell, superposition.bTransform);
53
+ }
54
+ // Apply coloring to other structure
55
+ if (coloring === 'subcomplex') {
56
+ yield Coloring.colorSubcomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, componentColors });
57
+ }
58
+ if (coloring === 'supercomplex') {
59
+ yield Coloring.colorSupercomplex(viewer, { otherStructId, baseComponents: baseComponents, otherComponents: otherComponents, baseMappings, otherMappings, coreColor, unmappedColor, componentColors });
60
+ }
61
+ // Adjust camera
62
+ const staticStructRef = viewer.getStructure(baseStructId);
63
+ const mobileStructRef = viewer.getStructure(otherStructId); // it is important to run this after superposition, to get correct coordinates for camera adjustment
64
+ // Wanted to use PluginCommands.Camera.Focus with viewer.plugin.canvas3d?.boundingSphere, but seems to not work properly, so using the following workaround:
65
+ yield commands_1.PluginCommands.Camera.FocusObject(viewer.plugin, {
66
+ targets: [
67
+ { targetRef: staticStructRef === null || staticStructRef === void 0 ? void 0 : staticStructRef.cell.transform.ref, extraRadius: 0.3 },
68
+ { 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 },
69
+ // 0.3 is amount by which bounding sphere algorithm usually underestimates actual visible bounding sphere
70
+ ],
71
+ durationMs: animationDuration,
72
+ });
73
+ yield (0, sleep_1.sleep)(animationDuration); // wait until camera adjustment completed
74
+ // Reveal new structure
75
+ yield viewer.visual.structureVisibility(otherStructId, true);
76
+ return {
77
+ /** Structure identifier of the newly loaded complex structure, to refer to this structure later */
78
+ id: otherStructId,
79
+ /** Status of pairwise superposition ('success' / 'zero-overlap' / 'failed') */
80
+ status,
81
+ /** Superposition RMSD and tranform (if status is 'success') */
82
+ superposition,
83
+ /** Function that deletes the newly loaded complex structure */
84
+ delete: () => viewer.deleteStructure(otherStructId),
85
+ };
86
+ });
87
+ }
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
+ */
@@ -0,0 +1,17 @@
1
+ import { MinimizeRmsd } from 'molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd';
2
+ import { Structure } from 'molstar/lib/mol-model/structure';
3
+ export type SuperpositionResult = {
4
+ status: 'success';
5
+ superposition: MinimizeRmsd.Result & {
6
+ nAlignedElements: number;
7
+ };
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;
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.superposeStructuresByBiggestCommonChain = superposeStructuresByBiggestCommonChain;
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
+ function extractUniprotIndex(structure, allowedAccessions) {
7
+ var _a, _b, _c;
8
+ var _d, _e;
9
+ const allowedAccessionsSet = allowedAccessions ? new Set(allowedAccessions) : undefined;
10
+ const seenUnitInvariantIds = new Set();
11
+ const out = {};
12
+ for (const unit of structure.units) {
13
+ if (seenUnitInvariantIds.has(unit.invariantId))
14
+ continue;
15
+ else
16
+ seenUnitInvariantIds.add(unit.invariantId);
17
+ const src = structure.model.sourceData;
18
+ if (!mmcif_1.MmcifFormat.is(src))
19
+ throw new Error('Source data must be mmCIF/BCIF');
20
+ const h = unit.model.atomicHierarchy;
21
+ const { pdbx_sifts_xref_db_acc, pdbx_sifts_xref_db_name, pdbx_sifts_xref_db_num } = src.data.db.atom_site;
22
+ const atoms = unit.polymerElements;
23
+ const nAtoms = atoms.length;
24
+ for (let i = 0; i < nAtoms; i++) {
25
+ const iAtom = atoms[i];
26
+ const srcIAtom = h.atomSourceIndex.value(iAtom);
27
+ const dbName = pdbx_sifts_xref_db_name.value(srcIAtom);
28
+ if (dbName !== 'UNP')
29
+ continue;
30
+ const dbAcc = pdbx_sifts_xref_db_acc.value(srcIAtom);
31
+ if (allowedAccessionsSet && !allowedAccessionsSet.has(dbAcc))
32
+ continue;
33
+ const dbNum = pdbx_sifts_xref_db_num.value(srcIAtom);
34
+ const structMapping = (_a = out[dbAcc]) !== null && _a !== void 0 ? _a : (out[dbAcc] = {});
35
+ const chainMapping = (_b = structMapping[_d = unit.id]) !== null && _b !== void 0 ? _b : (structMapping[_d] = {
36
+ label_asym_id: h.chains.label_asym_id.value(h.chainAtomSegments.index[atoms[0]]),
37
+ auth_asym_id: h.chains.auth_asym_id.value(h.chainAtomSegments.index[atoms[0]]),
38
+ atomMap: {},
39
+ });
40
+ (_c = (_e = chainMapping.atomMap)[dbNum]) !== null && _c !== void 0 ? _c : (_e[dbNum] = iAtom);
41
+ }
42
+ }
43
+ return out;
44
+ }
45
+ function bestUniprotMatch(a, b) {
46
+ const sortedA = sortAccessionAndChains(a);
47
+ const sortedB = sortAccessionAndChains(b);
48
+ let bestMatch = undefined;
49
+ let bestScore = 0;
50
+ for (const accession of sortedA.accessions) {
51
+ const unitsA = sortedA.units[accession];
52
+ const unitsB = sortedB.units[accession];
53
+ if (!unitsB)
54
+ continue;
55
+ for (const ua of unitsA) {
56
+ if (ua.size <= bestScore)
57
+ break;
58
+ const unitA = a[accession][ua.unitId];
59
+ for (const ub of unitsB) {
60
+ if (ub.size <= bestScore || ua.size <= bestScore)
61
+ break;
62
+ const unitB = b[accession][ub.unitId];
63
+ const score = objectKeyOverlap(unitA.atomMap, unitB.atomMap);
64
+ if (score > bestScore) {
65
+ bestScore = score;
66
+ bestMatch = { accession, unitA: ua.unitId, unitB: ub.unitId, nMatchedElements: score };
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return bestMatch;
72
+ }
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
+ /** Return number of keys common to objects `a` and `b` */
94
+ function objectKeyOverlap(a, b) {
95
+ let overlap = 0;
96
+ for (const key in a) {
97
+ if (key in b) {
98
+ overlap++;
99
+ }
100
+ }
101
+ return overlap;
102
+ }
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
+ }
@@ -1,6 +1,6 @@
1
1
  /** Helper functions to allow visualizing Foldseek results and superposing them on the query structure */
2
2
  import { Mat4 } from 'molstar/lib/mol-math/linear-algebra';
3
- import { type PDBeMolstarPlugin } from '../..';
3
+ import { PDBeMolstarPlugin } from '../..';
4
4
  export interface FoldseekApiData {
5
5
  query: string;
6
6
  target: string;
@@ -1,18 +1,20 @@
1
1
  "use strict";
2
2
  /** Helper functions to allow visualizing Foldseek results and superposing them on the query structure */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.exportModels = exports.loadFoldseekSuperposition = void 0;
4
+ exports.loadFoldseekSuperposition = loadFoldseekSuperposition;
5
+ exports.exportModels = exportModels;
5
6
  const tslib_1 = require("tslib");
6
7
  const export_1 = require("molstar/lib/extensions/model-export/export");
7
8
  const minimize_rmsd_1 = require("molstar/lib/mol-math/linear-algebra/3d/minimize-rmsd");
9
+ const __1 = require("../..");
8
10
  const helpers_1 = require("../../helpers");
9
11
  const superposition_1 = require("../../superposition");
10
12
  /** Load target structure as defined by `apiData`
11
13
  * and superpose it on the already loaded query structure. */
12
- function loadFoldseekSuperposition(viewer, targetStructId, apiData, targetColor = '#00ff00') {
13
- var _a, _b, _c, _d;
14
- return tslib_1.__awaiter(this, void 0, void 0, function* () {
15
- const Q_STRUCT_ID = 'main';
14
+ function loadFoldseekSuperposition(viewer_1, targetStructId_1, apiData_1) {
15
+ return tslib_1.__awaiter(this, arguments, void 0, function* (viewer, targetStructId, apiData, targetColor = '#00ff00') {
16
+ var _a, _b, _c, _d;
17
+ const Q_STRUCT_ID = __1.PDBeMolstarPlugin.MAIN_STRUCTURE_ID;
16
18
  // Retrieve target entry ID and chain ID
17
19
  let tEntryId;
18
20
  let tAuthAsymId;
@@ -69,7 +71,6 @@ function loadFoldseekSuperposition(viewer, targetStructId, apiData, targetColor
69
71
  return Object.assign(Object.assign({}, superposition), { nAligned: qCoords.x.length });
70
72
  });
71
73
  }
72
- exports.loadFoldseekSuperposition = loadFoldseekSuperposition;
73
74
  function getMatchedResidues(qstart, qaln, tstart, taln) {
74
75
  if (qaln.length !== taln.length) {
75
76
  throw new Error('Length of qaln and taln must be the same');
@@ -199,4 +200,3 @@ function discardMissingLocations(a, b) {
199
200
  function exportModels(viewer, format = 'cif') {
200
201
  return (0, export_1.exportHierarchy)(viewer.plugin, { format });
201
202
  }
202
- exports.exportModels = exportModels;
@@ -0,0 +1,36 @@
1
+ /** Helper functions to allow showing custom atom interactions */
2
+ import { PDBeMolstarPlugin } from '../..';
3
+ import { QueryParam } from '../../helpers';
4
+ export interface Interaction {
5
+ start: QueryParam;
6
+ end: QueryParam;
7
+ color?: string;
8
+ radius?: number;
9
+ dash_length?: number;
10
+ tooltip?: string;
11
+ }
12
+ export interface StateObjectHandle {
13
+ /** State transform reference */
14
+ ref: string;
15
+ /** Set state object visibility on/off */
16
+ setVisibility(visible: boolean): void;
17
+ /** Remove state object from state hierarchy */
18
+ delete: () => Promise<void>;
19
+ }
20
+ export declare function loadInteractions_example(viewer: PDBeMolstarPlugin, params?: {
21
+ opacity?: number;
22
+ color?: string;
23
+ radius?: number;
24
+ dash_length?: number;
25
+ }): Promise<StateObjectHandle>;
26
+ /** Show custom atom interactions */
27
+ export declare function loadInteractions(viewer: PDBeMolstarPlugin, params: {
28
+ interactions: Interaction[];
29
+ structureId?: string;
30
+ opacity?: number;
31
+ color?: string;
32
+ radius?: number;
33
+ dash_length?: number;
34
+ }): Promise<StateObjectHandle>;
35
+ /** Remove any previously added interactions */
36
+ export declare function clearInteractions(viewer: PDBeMolstarPlugin): Promise<void>;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ /** Helper functions to allow showing custom atom interactions */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.loadInteractions_example = loadInteractions_example;
5
+ exports.loadInteractions = loadInteractions;
6
+ exports.clearInteractions = clearInteractions;
7
+ const tslib_1 = require("tslib");
8
+ const primitives_1 = require("molstar/lib/extensions/mvs/components/primitives");
9
+ const mvs_data_1 = require("molstar/lib/extensions/mvs/mvs-data");
10
+ const representation_1 = require("molstar/lib/mol-plugin-state/transforms/representation");
11
+ const state_1 = require("molstar/lib/mol-plugin/behavior/static/state");
12
+ const __1 = require("../..");
13
+ const helpers_1 = require("../../helpers");
14
+ const plugin_custom_state_1 = require("../../plugin-custom-state");
15
+ /** Name used when registering extension, custom state, etc. */
16
+ const InteractionsExtensionName = 'pdbe-custom-interactions';
17
+ const getExtensionCustomState = plugin_custom_state_1.ExtensionCustomState.getter(InteractionsExtensionName);
18
+ const DEFAULT_COLOR = 'white';
19
+ const DEFAULT_RADIUS = 0.075;
20
+ const DEFAULT_DASH_LENGTH = 0.1;
21
+ const DEFAULT_OPACITY = 1;
22
+ function loadInteractions_example(viewer, params) {
23
+ return loadInteractions(viewer, Object.assign(Object.assign({}, params), { interactions: exampleData }));
24
+ }
25
+ /** Show custom atom interactions */
26
+ function loadInteractions(viewer, params) {
27
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
28
+ var _a, _b;
29
+ var _c;
30
+ const structureId = (_a = params.structureId) !== null && _a !== void 0 ? _a : __1.PDBeMolstarPlugin.MAIN_STRUCTURE_ID;
31
+ const struct = viewer.getStructure(structureId);
32
+ if (!struct)
33
+ throw new Error(`Did not find structure with ID "${structureId}"`);
34
+ const primitivesMvsNode = interactionsToMvsPrimitiveData(params);
35
+ const update = viewer.plugin.build();
36
+ const data = update.to(struct.cell).apply(primitives_1.MVSInlinePrimitiveData, { node: primitivesMvsNode }, { tags: ['custom-interactions-data'] });
37
+ data.apply(primitives_1.MVSBuildPrimitiveShape, { kind: 'mesh' }).apply(representation_1.ShapeRepresentation3D, {}, { tags: ['custom-interactions-mesh'] });
38
+ data.apply(primitives_1.MVSBuildPrimitiveShape, { kind: 'lines' }).apply(representation_1.ShapeRepresentation3D, {}, { tags: ['custom-interactions-lines'] });
39
+ data.apply(primitives_1.MVSBuildPrimitiveShape, { kind: 'labels' }).apply(representation_1.ShapeRepresentation3D, {}, { tags: ['custom-interactions-labels'] });
40
+ yield update.commit();
41
+ const visual = {
42
+ ref: data.ref,
43
+ setVisibility: (visible) => (0, state_1.setSubtreeVisibility)(viewer.plugin.state.data, data.ref, !visible /* true means hidden */),
44
+ delete: () => viewer.plugin.build().delete(data.ref).commit(),
45
+ };
46
+ const visualsList = (_b = (_c = getExtensionCustomState(viewer.plugin)).visuals) !== null && _b !== void 0 ? _b : (_c.visuals = []);
47
+ visualsList.push(visual);
48
+ return visual;
49
+ });
50
+ }
51
+ /** Remove any previously added interactions */
52
+ function clearInteractions(viewer) {
53
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
54
+ const visuals = getExtensionCustomState(viewer.plugin).visuals;
55
+ if (!visuals)
56
+ return;
57
+ for (const visual of visuals) {
58
+ yield visual.delete();
59
+ }
60
+ visuals.length = 0;
61
+ });
62
+ }
63
+ function interactionsToMvsPrimitiveData(params) {
64
+ var _a, _b, _c, _d, _e, _f, _g;
65
+ const builder = mvs_data_1.MVSData.createBuilder();
66
+ const primitives = builder.primitives({
67
+ opacity: (_a = params.opacity) !== null && _a !== void 0 ? _a : DEFAULT_OPACITY,
68
+ color: (_b = params.color) !== null && _b !== void 0 ? _b : DEFAULT_COLOR,
69
+ });
70
+ for (const interaction of params.interactions) {
71
+ primitives.tube({
72
+ start: { expressions: (0, helpers_1.queryParamsToMvsComponentExpressions)([interaction.start]) },
73
+ end: { expressions: (0, helpers_1.queryParamsToMvsComponentExpressions)([interaction.end]) },
74
+ radius: (_d = (_c = interaction.radius) !== null && _c !== void 0 ? _c : params.radius) !== null && _d !== void 0 ? _d : DEFAULT_RADIUS,
75
+ dash_length: (_f = (_e = interaction.dash_length) !== null && _e !== void 0 ? _e : params.dash_length) !== null && _f !== void 0 ? _f : DEFAULT_DASH_LENGTH,
76
+ color: interaction.color,
77
+ tooltip: interaction.tooltip,
78
+ });
79
+ }
80
+ const state = builder.getState();
81
+ const primitivesNode = (_g = state.root.children) === null || _g === void 0 ? void 0 : _g.find(child => child.kind === 'primitives');
82
+ if (!primitivesNode)
83
+ throw new Error('AssertionError: Failed to create MVS "primitives" subtree.');
84
+ return primitivesNode;
85
+ }
86
+ /** Selected interactions from https://www.ebi.ac.uk/pdbe/graph-api/pdb/bound_ligand_interactions/1hda/C/143 */
87
+ const exampleData = [
88
+ {
89
+ 'start': { 'auth_asym_id': 'C', 'auth_seq_id': 143, 'atoms': ['CBC'] },
90
+ 'end': { 'auth_asym_id': 'C', 'auth_seq_id': 32, 'atoms': ['CE'] },
91
+ 'color': 'yellow',
92
+ 'tooltip': '<strong>Hydrophobic interaction</strong><br>HEM 143 | CBC — MET 32 | CE',
93
+ },
94
+ {
95
+ 'start': { 'auth_asym_id': 'C', 'auth_seq_id': 143, 'atoms': ['CBC'] },
96
+ 'end': { 'auth_asym_id': 'C', 'auth_seq_id': 32, 'atoms': ['SD'] },
97
+ 'color': 'yellow',
98
+ 'tooltip': '<strong>Hydrophobic interaction</strong><br>HEM 143 | CBC — MET 32 | SD',
99
+ },
100
+ {
101
+ 'start': { 'auth_asym_id': 'C', 'auth_seq_id': 143, 'atoms': ['CMD'] },
102
+ 'end': { 'auth_asym_id': 'C', 'auth_seq_id': 42, 'atoms': ['O'] },
103
+ 'color': 'gray',
104
+ 'tooltip': '<strong>Mixed interaction</strong><br>Vdw, Weak polar<br>HEM 143 | CMD — TYR 42 | O',
105
+ },
106
+ {
107
+ 'start': { 'auth_asym_id': 'C', 'auth_seq_id': 143, 'atoms': ['C1B', 'C2B', 'C3B', 'C4B', 'NB'] },
108
+ 'end': { 'auth_asym_id': 'C', 'auth_seq_id': 136, 'atoms': ['CD1'] },
109
+ 'color': 'magenta',
110
+ 'tooltip': '<strong>CARBONPI interaction</strong><br>HEM 143 | C1B, C2B, C3B, C4B, NB — LEU 136 | CD1',
111
+ },
112
+ ];