pdbe-molstar 3.5.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.
- package/build/pdbe-molstar-component.js +2 -2
- package/build/pdbe-molstar-light.css +2 -2
- package/build/pdbe-molstar-plugin.js +2 -2
- package/build/pdbe-molstar-plugin.js.LICENSE.txt +1 -1
- package/build/pdbe-molstar.css +2 -2
- package/lib/extensions/complexes/coloring.d.ts +40 -0
- package/lib/extensions/complexes/coloring.js +118 -0
- package/lib/extensions/complexes/index.d.ts +55 -0
- package/lib/extensions/complexes/index.js +123 -0
- package/lib/extensions/complexes/superpose-by-biggest-chain.d.ts +17 -0
- package/lib/extensions/complexes/superpose-by-biggest-chain.js +135 -0
- package/lib/helpers.d.ts +4 -0
- package/lib/helpers.js +109 -64
- package/lib/spec.d.ts +1 -1
- package/lib/styles/pdbe-molstar/_index.scss +7 -7
- package/lib/styles/pdbe-molstar-dark.scss +2 -2
- package/lib/styles/pdbe-molstar-light.scss +2 -2
- package/lib/ui/pdbe-viewport-controls.js +3 -4
- package/lib/ui/pdbe-viewport.d.ts +5 -1
- package/lib/ui/pdbe-viewport.js +23 -2
- package/lib/ui/split-ui/components.d.ts +1 -1
- package/lib/ui/split-ui/components.js +2 -2
- package/lib/viewer.d.ts +7 -1
- package/lib/viewer.js +26 -13
- package/package.json +3 -3
|
@@ -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
|
+
}
|
package/lib/helpers.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
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';
|
|
5
6
|
import { PluginConfigItem } from 'molstar/lib/mol-plugin/config';
|
|
6
7
|
import { PluginContext } from 'molstar/lib/mol-plugin/context';
|
|
8
|
+
import { PluginLayoutStateProps } from 'molstar/lib/mol-plugin/layout';
|
|
7
9
|
import { Expression } from 'molstar/lib/mol-script/language/expression';
|
|
8
10
|
import { Overpaint } from 'molstar/lib/mol-theme/overpaint';
|
|
9
11
|
import { Color } from 'molstar/lib/mol-util/color';
|
|
@@ -192,4 +194,6 @@ export declare namespace PluginConfigUtils {
|
|
|
192
194
|
/** Retrieve config values for items in `configItems` from the current plugin config */
|
|
193
195
|
function getConfigValues<T extends object>(plugin: PluginContext | undefined, configItems: ConfigFor<T>, defaults: T): T;
|
|
194
196
|
}
|
|
197
|
+
export declare function pluginLayoutStateFromInitParams(initParams: InitParams): PluginLayoutStateProps;
|
|
198
|
+
export declare function getRotationMat4(view: 'front' | 'back' | 'right' | 'left' | 'top' | 'bottom' | Mat3): Mat4;
|
|
195
199
|
export {};
|
package/lib/helpers.js
CHANGED
|
@@ -13,13 +13,17 @@ exports.distinct = distinct;
|
|
|
13
13
|
exports.groupElements = groupElements;
|
|
14
14
|
exports.createIndex = createIndex;
|
|
15
15
|
exports.nonnegativeModulo = nonnegativeModulo;
|
|
16
|
+
exports.pluginLayoutStateFromInitParams = pluginLayoutStateFromInitParams;
|
|
17
|
+
exports.getRotationMat4 = getRotationMat4;
|
|
16
18
|
const tslib_1 = require("tslib");
|
|
17
19
|
const prop_1 = require("molstar/lib/extensions/model-archive/quality-assessment/prop");
|
|
20
|
+
const linear_algebra_1 = require("molstar/lib/mol-math/linear-algebra");
|
|
18
21
|
const structure_1 = require("molstar/lib/mol-model/structure");
|
|
19
22
|
const query_1 = require("molstar/lib/mol-model/structure/query/query");
|
|
20
23
|
const transforms_1 = require("molstar/lib/mol-plugin-state/transforms");
|
|
21
24
|
const transformers_1 = require("molstar/lib/mol-plugin/behavior/dynamic/volume-streaming/transformers");
|
|
22
25
|
const commands_1 = require("molstar/lib/mol-plugin/commands");
|
|
26
|
+
const layout_1 = require("molstar/lib/mol-plugin/layout");
|
|
23
27
|
const builder_1 = require("molstar/lib/mol-script/language/builder");
|
|
24
28
|
const compiler_1 = require("molstar/lib/mol-script/runtime/query/compiler");
|
|
25
29
|
const mol_state_1 = require("molstar/lib/mol-state");
|
|
@@ -197,92 +201,105 @@ var QueryHelper;
|
|
|
197
201
|
(function (QueryHelper) {
|
|
198
202
|
function getQueryObject(params, contextData) {
|
|
199
203
|
const selections = [];
|
|
200
|
-
|
|
201
|
-
let
|
|
202
|
-
|
|
203
|
-
|
|
204
|
+
/** `undefined` means SIFTSMappingMapping has not been retrieved yet. `null` means SIFTSMappingMapping has been retrieved unsuccessfully. */
|
|
205
|
+
let _siftMappings;
|
|
206
|
+
function getSiftsMappings() {
|
|
207
|
+
var _a;
|
|
208
|
+
if (_siftMappings === undefined)
|
|
209
|
+
_siftMappings = (_a = sifts_mapping_1.SIFTSMapping.Provider.get(contextData.models[0]).value) !== null && _a !== void 0 ? _a : null;
|
|
210
|
+
return _siftMappings;
|
|
211
|
+
}
|
|
212
|
+
for (const param of params) {
|
|
213
|
+
const predicates = {
|
|
214
|
+
entity: [],
|
|
215
|
+
chain: [],
|
|
216
|
+
residue: [],
|
|
217
|
+
atom: [],
|
|
218
|
+
};
|
|
204
219
|
// entity
|
|
205
|
-
if (param.entity_id)
|
|
206
|
-
|
|
220
|
+
if (param.entity_id !== undefined) {
|
|
221
|
+
predicates.entity.push(l => structure_1.StructureProperties.entity.id(l.element) === param.entity_id);
|
|
222
|
+
}
|
|
207
223
|
// chain
|
|
208
|
-
if (param.struct_asym_id) {
|
|
209
|
-
|
|
224
|
+
if (param.struct_asym_id !== undefined) {
|
|
225
|
+
predicates.chain.push(l => structure_1.StructureProperties.chain.label_asym_id(l.element) === param.struct_asym_id);
|
|
210
226
|
}
|
|
211
|
-
|
|
212
|
-
|
|
227
|
+
if (param.auth_asym_id !== undefined) {
|
|
228
|
+
predicates.chain.push(l => structure_1.StructureProperties.chain.auth_asym_id(l.element) === param.auth_asym_id);
|
|
213
229
|
}
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
selection['residueTest'] = l => structure_1.StructureProperties.atom.label_comp_id(l.element) === param.label_comp_id;
|
|
230
|
+
// residue
|
|
231
|
+
if (param.label_comp_id !== undefined) {
|
|
232
|
+
predicates.residue.push(l => structure_1.StructureProperties.atom.label_comp_id(l.element) === param.label_comp_id);
|
|
218
233
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (!siftMappings || currentAccession !== param.uniprot_accession) {
|
|
222
|
-
siftMappings = sifts_mapping_1.SIFTSMapping.Provider.get(contextData.models[0]).value;
|
|
223
|
-
currentAccession = param.uniprot_accession;
|
|
224
|
-
}
|
|
225
|
-
const rI = structure_1.StructureProperties.residue.key(l.element);
|
|
226
|
-
return !!siftMappings && param.uniprot_accession === siftMappings.accession[rI] && param.uniprot_residue_number === +siftMappings.num[rI];
|
|
227
|
-
};
|
|
234
|
+
if (param.residue_number !== undefined) {
|
|
235
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.label_seq_id(l.element) === param.residue_number);
|
|
228
236
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
if (!siftMappings || currentAccession !== param.uniprot_accession) {
|
|
232
|
-
siftMappings = sifts_mapping_1.SIFTSMapping.Provider.get(contextData.models[0]).value;
|
|
233
|
-
currentAccession = param.uniprot_accession;
|
|
234
|
-
}
|
|
235
|
-
const rI = structure_1.StructureProperties.residue.key(l.element);
|
|
236
|
-
return !!siftMappings && param.uniprot_accession === siftMappings.accession[rI] && (param.start_uniprot_residue_number <= +siftMappings.num[rI] && param.end_uniprot_residue_number >= +siftMappings.num[rI]);
|
|
237
|
-
};
|
|
237
|
+
if (param.start_residue_number !== undefined) {
|
|
238
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.label_seq_id(l.element) >= param.start_residue_number);
|
|
238
239
|
}
|
|
239
|
-
|
|
240
|
-
|
|
240
|
+
if (param.end_residue_number !== undefined) {
|
|
241
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.label_seq_id(l.element) <= param.end_residue_number);
|
|
241
242
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
const labelSeqId = structure_1.StructureProperties.residue.label_seq_id(l.element);
|
|
245
|
-
return labelSeqId >= param.start_residue_number && labelSeqId <= param.end_residue_number;
|
|
246
|
-
};
|
|
243
|
+
if (param.auth_seq_id !== undefined) {
|
|
244
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) === param.auth_seq_id);
|
|
247
245
|
}
|
|
248
|
-
|
|
249
|
-
|
|
246
|
+
if (param.auth_residue_number !== undefined) {
|
|
247
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) === param.auth_residue_number);
|
|
250
248
|
}
|
|
251
|
-
|
|
252
|
-
|
|
249
|
+
if (param.auth_ins_code_id !== undefined) {
|
|
250
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l.element) === param.auth_ins_code_id);
|
|
253
251
|
}
|
|
254
|
-
|
|
255
|
-
|
|
252
|
+
if (param.start_auth_residue_number !== undefined) {
|
|
253
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) >= param.start_auth_residue_number);
|
|
254
|
+
if (param.start_auth_ins_code_id !== undefined) {
|
|
255
|
+
// This assumes insertion code come in alphabetical order, which is not always true (e.g. 1ucy chain A [auth L]). However, authors of such monstrosities do not deserve their structures to be displayed correctly.
|
|
256
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) > param.start_auth_residue_number || structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l.element) >= param.start_auth_ins_code_id);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (param.end_auth_residue_number !== undefined) {
|
|
260
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) <= param.end_auth_residue_number);
|
|
261
|
+
if (param.end_auth_ins_code_id !== undefined) {
|
|
262
|
+
// This assumes insertion code come in alphabetical order, which is not always true (e.g. 1ucy chain A [auth L]). However, authors of such monstrosities do not deserve their structures to be displayed correctly.
|
|
263
|
+
predicates.residue.push(l => structure_1.StructureProperties.residue.auth_seq_id(l.element) < param.end_auth_residue_number || structure_1.StructureProperties.residue.pdbx_PDB_ins_code(l.element) <= param.end_auth_ins_code_id);
|
|
264
|
+
}
|
|
256
265
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
// TODO really check ins code, same for auth_seq_id, start_auth_ins_code_id, end_auth_ins_code_id
|
|
266
|
+
if (param.uniprot_accession !== undefined) {
|
|
267
|
+
predicates.residue.push(l => { var _a; return ((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.accession[structure_1.StructureProperties.residue.key(l.element)]) === param.uniprot_accession; });
|
|
260
268
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const authSeqId = structure_1.StructureProperties.residue.auth_seq_id(l.element);
|
|
264
|
-
return authSeqId >= param.start_auth_residue_number && authSeqId <= param.end_auth_residue_number;
|
|
265
|
-
};
|
|
269
|
+
if (param.uniprot_residue_number !== undefined) {
|
|
270
|
+
predicates.residue.push(l => { var _a; return Number((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.num[structure_1.StructureProperties.residue.key(l.element)]) === param.uniprot_residue_number; });
|
|
266
271
|
}
|
|
267
|
-
|
|
268
|
-
|
|
272
|
+
if (param.start_uniprot_residue_number !== undefined) {
|
|
273
|
+
predicates.residue.push(l => { var _a; return Number((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.num[structure_1.StructureProperties.residue.key(l.element)]) >= param.start_uniprot_residue_number; });
|
|
269
274
|
}
|
|
270
|
-
|
|
275
|
+
if (param.end_uniprot_residue_number !== undefined) {
|
|
276
|
+
predicates.residue.push(l => { var _a; return Number((_a = getSiftsMappings()) === null || _a === void 0 ? void 0 : _a.num[structure_1.StructureProperties.residue.key(l.element)]) <= param.end_uniprot_residue_number; });
|
|
277
|
+
}
|
|
278
|
+
// atom
|
|
271
279
|
if (param.atoms) {
|
|
272
|
-
|
|
280
|
+
predicates.atom.push(l => param.atoms.includes(structure_1.StructureProperties.atom.label_atom_id(l.element)));
|
|
273
281
|
}
|
|
274
282
|
if (param.atom_id) {
|
|
275
|
-
|
|
283
|
+
predicates.atom.push(l => param.atom_id.includes(structure_1.StructureProperties.atom.id(l.element)));
|
|
276
284
|
}
|
|
277
|
-
selections.push(
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
285
|
+
selections.push({
|
|
286
|
+
entityTest: predicateConjunction(predicates.entity),
|
|
287
|
+
chainTest: predicateConjunction(predicates.chain),
|
|
288
|
+
residueTest: predicateConjunction(predicates.residue),
|
|
289
|
+
atomTest: predicateConjunction(predicates.atom),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
const atmGroupsQueries = selections.map(selection => structure_1.Queries.generators.atoms(selection));
|
|
283
293
|
return structure_1.Queries.combinators.merge(atmGroupsQueries);
|
|
284
294
|
}
|
|
285
295
|
QueryHelper.getQueryObject = getQueryObject;
|
|
296
|
+
function predicateConjunction(predicates) {
|
|
297
|
+
if (predicates.length === 0)
|
|
298
|
+
return undefined;
|
|
299
|
+
if (predicates.length === 1)
|
|
300
|
+
return predicates[0];
|
|
301
|
+
return (x => predicates.every(pred => pred(x)));
|
|
302
|
+
}
|
|
286
303
|
function getInteractivityLoci(params, contextData) {
|
|
287
304
|
const sel = query_1.StructureQuery.run(QueryHelper.getQueryObject(params, contextData), contextData);
|
|
288
305
|
return structure_1.StructureSelection.toLociWithSourceUnits(sel);
|
|
@@ -570,3 +587,31 @@ var PluginConfigUtils;
|
|
|
570
587
|
}
|
|
571
588
|
PluginConfigUtils.getConfigValues = getConfigValues;
|
|
572
589
|
})(PluginConfigUtils || (exports.PluginConfigUtils = PluginConfigUtils = {}));
|
|
590
|
+
function pluginLayoutStateFromInitParams(initParams) {
|
|
591
|
+
return {
|
|
592
|
+
isExpanded: initParams.expanded,
|
|
593
|
+
showControls: !initParams.hideControls,
|
|
594
|
+
regionState: {
|
|
595
|
+
left: initParams.leftPanel ? 'full' : 'hidden',
|
|
596
|
+
right: initParams.rightPanel ? 'full' : 'hidden',
|
|
597
|
+
top: initParams.sequencePanel ? 'full' : 'hidden',
|
|
598
|
+
bottom: initParams.logPanel ? 'full' : 'hidden',
|
|
599
|
+
},
|
|
600
|
+
controlsDisplay: initParams.reactive ? 'reactive' : initParams.landscape ? 'landscape' : layout_1.PluginLayoutStateParams.controlsDisplay.defaultValue,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function getRotationMat4(view) {
|
|
604
|
+
switch (view) {
|
|
605
|
+
case 'front': return linear_algebra_1.Mat4.identity();
|
|
606
|
+
case 'back': return [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1];
|
|
607
|
+
case 'right': return [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1];
|
|
608
|
+
case 'left': return [0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1];
|
|
609
|
+
case 'top': return [1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1];
|
|
610
|
+
case 'bottom': return [1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1];
|
|
611
|
+
default: {
|
|
612
|
+
const out = linear_algebra_1.Mat4.fromMat3((0, linear_algebra_1.Mat4)(), view);
|
|
613
|
+
out[15] = 1;
|
|
614
|
+
return out;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
package/lib/spec.d.ts
CHANGED
|
@@ -123,7 +123,7 @@ export interface InitParams {
|
|
|
123
123
|
/** Hide all control panels by default (can be shown by the Toggle Controls Panel button (wrench icon)) */
|
|
124
124
|
hideControls: boolean;
|
|
125
125
|
/** Hide individual icon buttons in the top-right corner of the canvas */
|
|
126
|
-
hideCanvasControls: ('expand' | 'controlToggle' | 'controlInfo' | 'selection' | 'animation' | 'trajectory')[];
|
|
126
|
+
hideCanvasControls: ('screenshot' | 'expand' | 'controlToggle' | 'controlInfo' | 'selection' | 'animation' | 'trajectory')[];
|
|
127
127
|
/** Display Sequence panel */
|
|
128
128
|
sequencePanel: boolean;
|
|
129
129
|
/** Display Left control panel */
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// Color constants must be imported before importing this file
|
|
4
4
|
// (from molstar/lib/mol-plugin-ui/skin/*.scss or molstar/lib/mol-plugin-ui/skin/colors/*.scss)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
@import 'molstar/lib/mol-plugin-ui/skin/base/variables.scss';
|
|
6
|
+
@use 'molstar/lib/mol-plugin-ui/skin/base/_vars.scss' as vars;
|
|
8
7
|
|
|
8
|
+
.msp-plugin {
|
|
9
9
|
::-webkit-scrollbar-thumb {
|
|
10
10
|
background-color: #80808080;
|
|
11
11
|
border-radius: 10px;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
.msp-viewport-controls-panel .msp-viewport-controls-panel-controls {
|
|
25
|
-
max-height: calc(100cqh -
|
|
25
|
+
max-height: calc(100cqh - 46px - 24px - 10px); // 100cqh = viewport container, 46px = control buttons offset, 24px = header, 10px = space below
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// Avoid wraping headers in left panel help
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
float: left;
|
|
44
44
|
|
|
45
45
|
&:not(:empty) {
|
|
46
|
-
margin-right:
|
|
46
|
+
margin-right: vars.$control-spacing;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
display: flex;
|
|
53
53
|
flex-direction: column;
|
|
54
54
|
align-items: center;
|
|
55
|
-
padding-inline: calc(54px + 2 *
|
|
55
|
+
padding-inline: calc(54px + 2 * vars.$control-spacing); // 54px = PDBe logo box
|
|
56
56
|
pointer-events: none;
|
|
57
57
|
|
|
58
58
|
>* {
|
|
59
|
-
margin-top:
|
|
59
|
+
margin-top: vars.$control-spacing;
|
|
60
60
|
pointer-events: auto;
|
|
61
61
|
max-width: 100%;
|
|
62
62
|
}
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
.pdbemolstar-state-gallery-title-box {
|
|
146
146
|
width: 500px;
|
|
147
147
|
max-width: 100%;
|
|
148
|
-
background-color:
|
|
148
|
+
background-color: vars.$msp-form-control-background;
|
|
149
149
|
display: flex;
|
|
150
150
|
flex-direction: row;
|
|
151
151
|
justify-content: space-between;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
@
|
|
2
|
-
@
|
|
1
|
+
@use 'molstar/lib/mol-plugin-ui/skin/dark';
|
|
2
|
+
@use './pdbe-molstar/_index';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
@
|
|
2
|
-
@
|
|
1
|
+
@use 'molstar/lib/mol-plugin-ui/skin/light';
|
|
2
|
+
@use './pdbe-molstar/_index';
|
|
@@ -17,9 +17,8 @@ class PDBeViewportControls extends viewport_1.ViewportControls {
|
|
|
17
17
|
const showPDBeLink = (initParams === null || initParams === void 0 ? void 0 : initParams.moleculeId) && (initParams === null || initParams === void 0 ? void 0 : initParams.pdbeLink) && !(initParams === null || initParams === void 0 ? void 0 : initParams.superposition);
|
|
18
18
|
const pdbeLinkColor = this.isBlack() ? '#fff' : '#555';
|
|
19
19
|
const pdbeLink = {
|
|
20
|
-
|
|
21
|
-
bgStyle: { position: 'absolute', height: '
|
|
22
|
-
containerStyle: { position: 'absolute', right: '10px', top: '10px', padding: '3px 3px 3px 18px' },
|
|
20
|
+
containerStyle: { position: 'absolute', right: '10px', top: '10px', padding: '6px', paddingRight: '3px', paddingLeft: '18px' },
|
|
21
|
+
bgStyle: { position: 'absolute', height: '32px', width: '54px', marginLeft: '-33px' },
|
|
23
22
|
style: { display: 'inline-block', fontSize: '14px', color: pdbeLinkColor, borderBottom: 'none', cursor: 'pointer', textDecoration: 'none', position: 'absolute', right: '5px' },
|
|
24
23
|
pdbeImg: {
|
|
25
24
|
src: 'https://www.ebi.ac.uk/pdbe/entry/static/images/logos/PDBe/logo_T_64.png',
|
|
@@ -27,7 +26,7 @@ class PDBeViewportControls extends viewport_1.ViewportControls {
|
|
|
27
26
|
style: { height: '12px', width: '12px', border: 0, position: 'absolute', margin: '4px 0 0 -13px' },
|
|
28
27
|
},
|
|
29
28
|
};
|
|
30
|
-
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showPDBeLink && (0, jsx_runtime_1.jsxs)("div", { className: 'msp-viewport-controls-buttons', style: pdbeLink.containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { className: 'msp-semi-transparent-background', style: pdbeLink.bgStyle }), (0, jsx_runtime_1.jsxs)("a", { className: 'msp-pdbe-link', style: pdbeLink.style, target: "_blank", href: `https://pdbe.org/${initParams.moleculeId}`, children: [(0, jsx_runtime_1.jsx)("img", { src: pdbeLink.pdbeImg.src, alt: pdbeLink.pdbeImg.alt, style: pdbeLink.pdbeImg.style }), initParams.moleculeId] })] }), (0, jsx_runtime_1.jsx)("div", { style: { position: 'absolute', top: showPDBeLink ? (
|
|
29
|
+
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [showPDBeLink && (0, jsx_runtime_1.jsxs)("div", { className: 'msp-viewport-controls-buttons', style: pdbeLink.containerStyle, children: [(0, jsx_runtime_1.jsx)("div", { className: 'msp-semi-transparent-background', style: pdbeLink.bgStyle }), (0, jsx_runtime_1.jsxs)("a", { className: 'msp-pdbe-link', style: pdbeLink.style, target: "_blank", href: `https://pdbe.org/${initParams.moleculeId}`, children: [(0, jsx_runtime_1.jsx)("img", { src: pdbeLink.pdbeImg.src, alt: pdbeLink.pdbeImg.alt, style: pdbeLink.pdbeImg.style }), initParams.moleculeId] })] }), (0, jsx_runtime_1.jsx)("div", { style: { position: 'absolute', top: showPDBeLink ? (32 + 4) : 0, right: 0 }, children: super.render() })] });
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
32
|
exports.PDBeViewportControls = PDBeViewportControls;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { DefaultViewport } from 'molstar/lib/mol-plugin-ui/plugin';
|
|
2
|
+
import { ComponentClass } from 'react';
|
|
2
3
|
/** A modified copy of DefaultViewport */
|
|
3
4
|
export declare class CustomizableDefaultViewport extends DefaultViewport {
|
|
4
5
|
render(): import("react/jsx-runtime").JSX.Element;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
+
/** Version of `PDBeViewport` to use as part of other components. Does not expand to fullscreen individually. */
|
|
8
|
+
export declare const PDBeViewport_NoFullscreen: ComponentClass<{}, any>;
|
|
9
|
+
/** Component containing 3D canvas, button in top left and top right corners, and tooltip box (center panel in default layout). Changes to fullscreen view by "Toggle Expanded Viewport" button, or "expanded" option. */
|
|
10
|
+
export declare const PDBeViewport: ComponentClass<{}, any>;
|