@czi-sds/data-viz 2.2.0 → 2.3.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/dist/index.cjs.d.ts +49 -11
- package/dist/index.cjs.js +590 -290
- package/dist/index.esm.d.ts +49 -11
- package/dist/index.esm.js +588 -291
- package/package.json +2 -2
package/dist/index.cjs.js
CHANGED
|
@@ -31,9 +31,8 @@ let react_jsx_runtime = require("react/jsx-runtime");
|
|
|
31
31
|
let _czi_sds_components = require("@czi-sds/components");
|
|
32
32
|
let _mui_material_styles = require("@mui/material/styles");
|
|
33
33
|
let _phosphor_icons_react = require("@phosphor-icons/react");
|
|
34
|
-
let molstar_lib_mol_util_color = require("molstar/lib/mol-util/color");
|
|
35
|
-
let molstar_lib_mol_data_int = require("molstar/lib/mol-data/int");
|
|
36
34
|
let molstar_lib_mol_model_structure = require("molstar/lib/mol-model/structure");
|
|
35
|
+
let molstar_lib_mol_plugin_state_helpers_structure_transparency = require("molstar/lib/mol-plugin-state/helpers/structure-transparency");
|
|
37
36
|
let molstar_lib_mol_plugin_ui = require("molstar/lib/mol-plugin-ui");
|
|
38
37
|
let molstar_lib_mol_plugin_ui_react18 = require("molstar/lib/mol-plugin-ui/react18");
|
|
39
38
|
let molstar_lib_mol_plugin_ui_spec = require("molstar/lib/mol-plugin-ui/spec");
|
|
@@ -46,6 +45,7 @@ let molstar_lib_mol_model_loci = require("molstar/lib/mol-model/loci");
|
|
|
46
45
|
let molstar_lib_mol_plugin_ui_base = require("molstar/lib/mol-plugin-ui/base");
|
|
47
46
|
let molstar_lib_mol_util_input_input_observer = require("molstar/lib/mol-util/input/input-observer");
|
|
48
47
|
let rxjs_operators = require("rxjs/operators");
|
|
48
|
+
let molstar_lib_mol_data_int = require("molstar/lib/mol-data/int");
|
|
49
49
|
let _mui_material = require("@mui/material");
|
|
50
50
|
let molstar_lib_mol_plugin_state_objects = require("molstar/lib/mol-plugin-state/objects");
|
|
51
51
|
let molstar_lib_mol_plugin_ui_sequence = require("molstar/lib/mol-plugin-ui/sequence");
|
|
@@ -54,6 +54,7 @@ let molstar_lib_mol_util = require("molstar/lib/mol-util");
|
|
|
54
54
|
let molstar_lib_mol_util_param_definition = require("molstar/lib/mol-util/param-definition");
|
|
55
55
|
let molstar_lib_mol_plugin_ui_toast = require("molstar/lib/mol-plugin-ui/toast");
|
|
56
56
|
let molstar_lib_mol_plugin_ui_viewport = require("molstar/lib/mol-plugin-ui/viewport");
|
|
57
|
+
let molstar_lib_mol_util_color = require("molstar/lib/mol-util/color");
|
|
57
58
|
let molstar_lib_mol_plugin_commands = require("molstar/lib/mol-plugin/commands");
|
|
58
59
|
let molstar_lib_mol_math_linear_algebra = require("molstar/lib/mol-math/linear-algebra");
|
|
59
60
|
let molstar_lib_mol_model_structure_model_types = require("molstar/lib/mol-model/structure/model/types");
|
|
@@ -1231,249 +1232,183 @@ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChai
|
|
|
1231
1232
|
});
|
|
1232
1233
|
}
|
|
1233
1234
|
//#endregion
|
|
1234
|
-
//#region
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
function selectionKey(selection) {
|
|
1244
|
-
var _selection$residues, _selection$chains;
|
|
1245
|
-
const residues = [...new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : [])].sort((a, b) => a - b);
|
|
1246
|
-
return `${[...new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : [])].sort().join(",")}|${residues.join(",")}`;
|
|
1235
|
+
//#region \0@oxc-project+runtime@0.134.0/helpers/esm/asyncToGenerator.js
|
|
1236
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
1237
|
+
try {
|
|
1238
|
+
var i = n[a](c), u = i.value;
|
|
1239
|
+
} catch (n) {
|
|
1240
|
+
e(n);
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
1247
1244
|
}
|
|
1248
|
-
|
|
1249
|
-
function
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1245
|
+
function _asyncToGenerator(n) {
|
|
1246
|
+
return function() {
|
|
1247
|
+
var t = this, e = arguments;
|
|
1248
|
+
return new Promise(function(r, o) {
|
|
1249
|
+
var a = n.apply(t, e);
|
|
1250
|
+
function _next(n) {
|
|
1251
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
1252
|
+
}
|
|
1253
|
+
function _throw(n) {
|
|
1254
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
1255
|
+
}
|
|
1256
|
+
_next(void 0);
|
|
1257
|
+
});
|
|
1258
|
+
};
|
|
1253
1259
|
}
|
|
1260
|
+
//#endregion
|
|
1261
|
+
//#region src/core/ProteinStructureViewer/hooks/useChainHighlight.ts
|
|
1254
1262
|
/**
|
|
1255
|
-
*
|
|
1263
|
+
* How transparent the other chains go while one is pointed at.
|
|
1256
1264
|
*
|
|
1257
|
-
*
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
1260
|
-
* selected chain is counted once.
|
|
1265
|
+
* Enough that the chain under the pointer is the one being read, not enough
|
|
1266
|
+
* that the rest of the complex stops being context for it. 0 is opaque, 1 is
|
|
1267
|
+
* invisible.
|
|
1261
1268
|
*/
|
|
1262
|
-
|
|
1263
|
-
var _selection$residues3, _selection$chains3;
|
|
1264
|
-
const residues = new Set((_selection$residues3 = selection.residues) !== null && _selection$residues3 !== void 0 ? _selection$residues3 : []);
|
|
1265
|
-
for (const chainId of (_selection$chains3 = selection.chains) !== null && _selection$chains3 !== void 0 ? _selection$chains3 : []) {
|
|
1266
|
-
var _residuesByChain$get;
|
|
1267
|
-
for (const index of (_residuesByChain$get = residuesByChain.get(chainId)) !== null && _residuesByChain$get !== void 0 ? _residuesByChain$get : []) residues.add(index);
|
|
1268
|
-
}
|
|
1269
|
-
return [...residues].sort((a, b) => a - b);
|
|
1270
|
-
}
|
|
1269
|
+
const CHAIN_DIM_TRANSPARENCY = .65;
|
|
1271
1270
|
/**
|
|
1272
|
-
*
|
|
1271
|
+
* The chain id in a component key.
|
|
1273
1272
|
*
|
|
1274
|
-
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
* named
|
|
1278
|
-
* three-letter code and the number the file gives it.
|
|
1273
|
+
* Chain components are tagged `structure-component-<part>-<chainId>`, and a
|
|
1274
|
+
* key is the component's tags sorted and joined with commas. Anchoring on the
|
|
1275
|
+
* part name and stopping at the comma is what keeps `A` from also matching the
|
|
1276
|
+
* chain named `AA`.
|
|
1279
1277
|
*/
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1278
|
+
const COMPONENT_CHAIN_ID = /structure-component-(?:polymer|ligand)-([^,]+)/;
|
|
1279
|
+
/** The chain a component was built for, or undefined if it is not a chain's. */
|
|
1280
|
+
function componentChainId(component) {
|
|
1281
|
+
var _component$key, _COMPONENT_CHAIN_ID$e;
|
|
1282
|
+
const key = (_component$key = component.key) !== null && _component$key !== void 0 ? _component$key : component.cell.transform.ref;
|
|
1283
|
+
return (_COMPONENT_CHAIN_ID$e = COMPONENT_CHAIN_ID.exec(key)) === null || _COMPONENT_CHAIN_ID$e === void 0 ? void 0 : _COMPONENT_CHAIN_ID$e[1];
|
|
1285
1284
|
}
|
|
1286
|
-
/**
|
|
1287
|
-
function
|
|
1288
|
-
|
|
1289
|
-
let count = 0;
|
|
1290
|
-
for (const index of residues) {
|
|
1291
|
-
const value = read(index);
|
|
1292
|
-
if (value === null || value === void 0) continue;
|
|
1293
|
-
sum += value;
|
|
1294
|
-
count++;
|
|
1295
|
-
}
|
|
1296
|
-
return count === 0 ? null : sum / count;
|
|
1285
|
+
/** Every chain component of every loaded structure, polymers and ligands. */
|
|
1286
|
+
function structureComponents(plugin) {
|
|
1287
|
+
return plugin.managers.structure.hierarchy.current.structures.flatMap((entry) => entry.components);
|
|
1297
1288
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
* Every atom the predicate accepts, as geometry Mol* can focus, frame and mark,
|
|
1302
|
-
* or undefined when it accepts none.
|
|
1303
|
-
*
|
|
1304
|
-
* One pass over the structure rather than a pass per residue: a whole-chain
|
|
1305
|
-
* selection names hundreds of residues, and resolving them one at a time would
|
|
1306
|
-
* walk the structure once for each.
|
|
1307
|
-
*
|
|
1308
|
-
* Atoms are bucketed by the unit they belong to because element indices are
|
|
1309
|
-
* unit-local. That bucketing is also what lets a result span a chain break: it
|
|
1310
|
-
* carries an entry per unit, which is what a range across two chains needs and
|
|
1311
|
-
* what a single unit-local entry cannot express.
|
|
1312
|
-
*
|
|
1313
|
-
* Whole residues are collected rather than one atom each, since the caller
|
|
1314
|
-
* frames the camera on the loci's bounding sphere and a lone atom would zoom
|
|
1315
|
-
* far closer than the residue warrants.
|
|
1316
|
-
*/
|
|
1317
|
-
function lociMatching(structure, matches) {
|
|
1318
|
-
const location = molstar_lib_mol_model_structure.StructureElement.Location.create(structure);
|
|
1319
|
-
const elements = [];
|
|
1320
|
-
for (const unit of structure.units) {
|
|
1321
|
-
location.unit = unit;
|
|
1322
|
-
const indices = [];
|
|
1323
|
-
for (let i = 0; i < unit.elements.length; i++) {
|
|
1324
|
-
location.element = unit.elements[i];
|
|
1325
|
-
if (matches(location)) indices.push(i);
|
|
1326
|
-
}
|
|
1327
|
-
if (indices.length > 0) elements.push({
|
|
1328
|
-
indices,
|
|
1329
|
-
unit
|
|
1330
|
-
});
|
|
1331
|
-
}
|
|
1332
|
-
if (elements.length === 0) return void 0;
|
|
1333
|
-
return molstar_lib_mol_model_structure.StructureElement.Loci(structure, elements.map(({ indices, unit }) => ({
|
|
1334
|
-
indices: molstar_lib_mol_data_int.OrderedSet.ofSortedArray(indices),
|
|
1335
|
-
unit
|
|
1336
|
-
})));
|
|
1289
|
+
/** A set of chain ids as one comparable string. */
|
|
1290
|
+
function chainsKey(chains) {
|
|
1291
|
+
return [...chains].sort().join("\0");
|
|
1337
1292
|
}
|
|
1338
1293
|
/**
|
|
1339
|
-
*
|
|
1294
|
+
* Makes every chain outside `lit` transparent, or all of them opaque again when
|
|
1295
|
+
* nothing is lit.
|
|
1340
1296
|
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1343
|
-
*
|
|
1344
|
-
* part of the state tree the chain's own visibility does not reach, and a
|
|
1345
|
-
* hidden chain left in the loci would keep showing up there after its cartoon
|
|
1346
|
-
* had gone.
|
|
1347
|
-
*/
|
|
1348
|
-
function lociForSelectionInStructure(structure, selection, hiddenChains) {
|
|
1349
|
-
var _selection$residues, _selection$chains;
|
|
1350
|
-
if (isEmptySelection(selection)) return void 0;
|
|
1351
|
-
const residues = new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : []);
|
|
1352
|
-
const chains = new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : []);
|
|
1353
|
-
return lociMatching(structure, (location) => {
|
|
1354
|
-
const chainId = molstar_lib_mol_model_structure.StructureProperties.chain.auth_asym_id(location);
|
|
1355
|
-
if (hiddenChains === null || hiddenChains === void 0 ? void 0 : hiddenChains.has(chainId)) return false;
|
|
1356
|
-
return residues.has(molstar_lib_mol_model_structure.StructureProperties.residue.key(location)) || chains.has(chainId);
|
|
1357
|
-
});
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Every atom of one chain's polymer, in the first structure that has one.
|
|
1297
|
+
* Transparency layers accumulate, so what is already there is cleared before
|
|
1298
|
+
* the next chain is dimmed rather than added to - otherwise moving along the
|
|
1299
|
+
* list would leave every chain it passed dimmed behind it.
|
|
1361
1300
|
*
|
|
1362
|
-
*
|
|
1363
|
-
*
|
|
1364
|
-
*
|
|
1365
|
-
* own.
|
|
1301
|
+
* False when there is no structure to write to yet, which is what a selection
|
|
1302
|
+
* arriving before the first load looks like. It is applied again after the load
|
|
1303
|
+
* rather than being taken as done.
|
|
1366
1304
|
*/
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
var _entry$cell$obj;
|
|
1370
|
-
const structure = (_entry$cell$obj = entry.cell.obj) === null || _entry$cell$obj === void 0 ? void 0 : _entry$cell$obj.data;
|
|
1371
|
-
if (!structure) continue;
|
|
1372
|
-
const loci = lociMatching(structure, (location) => molstar_lib_mol_model_structure.StructureProperties.chain.auth_asym_id(location) === chainId && molstar_lib_mol_model_structure.StructureProperties.entity.type(location) === "polymer");
|
|
1373
|
-
if (loci) return loci;
|
|
1374
|
-
}
|
|
1305
|
+
function dimChainsOutside(_x2, _x3) {
|
|
1306
|
+
return _dimChainsOutside.apply(this, arguments);
|
|
1375
1307
|
}
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1308
|
+
function _dimChainsOutside() {
|
|
1309
|
+
_dimChainsOutside = _asyncToGenerator(function* (plugin, lit) {
|
|
1310
|
+
const components = structureComponents(plugin);
|
|
1311
|
+
if (components.length === 0) return false;
|
|
1312
|
+
yield (0, molstar_lib_mol_plugin_state_helpers_structure_transparency.clearStructureTransparency)(plugin, components);
|
|
1313
|
+
if (lit.size === 0) return true;
|
|
1314
|
+
const dimmed = components.filter((component) => {
|
|
1315
|
+
const chainId = componentChainId(component);
|
|
1316
|
+
return chainId !== void 0 && !lit.has(chainId);
|
|
1317
|
+
});
|
|
1318
|
+
if (dimmed.length === 0) return true;
|
|
1319
|
+
yield (0, molstar_lib_mol_plugin_state_helpers_structure_transparency.setStructureTransparency)(plugin, dimmed, CHAIN_DIM_TRANSPARENCY, function() {
|
|
1320
|
+
var _ref = _asyncToGenerator(function* (structure) {
|
|
1321
|
+
return molstar_lib_mol_model_structure.Structure.toStructureElementLoci(structure);
|
|
1322
|
+
});
|
|
1323
|
+
return function(_x) {
|
|
1324
|
+
return _ref.apply(this, arguments);
|
|
1325
|
+
};
|
|
1326
|
+
}());
|
|
1327
|
+
return true;
|
|
1387
1328
|
});
|
|
1329
|
+
return _dimChainsOutside.apply(this, arguments);
|
|
1388
1330
|
}
|
|
1389
1331
|
/**
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*/
|
|
1394
|
-
function lociForSelection(plugin, selection, hiddenChains) {
|
|
1395
|
-
for (const entry of plugin.managers.structure.hierarchy.current.structures) {
|
|
1396
|
-
var _entry$cell$obj2;
|
|
1397
|
-
const structure = (_entry$cell$obj2 = entry.cell.obj) === null || _entry$cell$obj2 === void 0 ? void 0 : _entry$cell$obj2.data;
|
|
1398
|
-
if (!structure) continue;
|
|
1399
|
-
const loci = lociForSelectionInStructure(structure, selection, hiddenChains);
|
|
1400
|
-
if (loci) return loci;
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
//#endregion
|
|
1404
|
-
//#region src/core/ProteinStructureViewer/hooks/useChainHighlight.ts
|
|
1405
|
-
/**
|
|
1406
|
-
* What a chain lights up in while its name is under the pointer.
|
|
1332
|
+
* Leaves one chain lit and dims the rest: the chain whose name is pointed at,
|
|
1333
|
+
* in the sequence panel's captions or the chain legend's rows, or the chain the
|
|
1334
|
+
* selection covers once the pointer has gone.
|
|
1407
1335
|
*
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
*
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
* overlay. A saturated pink is in none of those, so it cannot be mistaken for
|
|
1414
|
-
* the structure's own coloring.
|
|
1415
|
-
*/
|
|
1416
|
-
const CHAIN_HIGHLIGHT_COLOR = molstar_lib_mol_util_color.Color.fromRgb(200, 45, 149);
|
|
1417
|
-
/**
|
|
1418
|
-
* Stronger than the residue hover's tint, for the same reason. A whole chain
|
|
1419
|
-
* washed in 20% pink reads as a rendering artifact; at this strength it reads
|
|
1420
|
-
* as deliberate.
|
|
1421
|
-
*/
|
|
1422
|
-
const CHAIN_HIGHLIGHT_STRENGTH = .7;
|
|
1423
|
-
/**
|
|
1424
|
-
* Lights up a whole chain in the 3D view while its name is pointed at, in the
|
|
1425
|
-
* sequence panel's captions or the chain legend's rows.
|
|
1336
|
+
* The chain left lit is exactly as it was - in the chain palette, in pLDDT
|
|
1337
|
+
* bands, or in an overlay - and the others recede around it. Nothing is painted
|
|
1338
|
+
* over the top, so what the reader is being pointed at is still the color the
|
|
1339
|
+
* legend beside it is describing, and a selected chain reads the same as a
|
|
1340
|
+
* hovered one rather than sprouting a highlight of its own.
|
|
1426
1341
|
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1429
|
-
* and nothing to tear down. Marking colors are a property of the renderer
|
|
1430
|
-
* rather than of the loci, though, so the pink is swapped in for the length of
|
|
1431
|
-
* the hover and the theme's own colors handed back on the way out.
|
|
1342
|
+
* A hover outranks the selection while it lasts, since pointing at a chain is a
|
|
1343
|
+
* question about that chain, and hands it back on the way out.
|
|
1432
1344
|
*
|
|
1433
|
-
*
|
|
1434
|
-
*
|
|
1435
|
-
*
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1345
|
+
* Dimming is a state-tree commit rather than a renderer setting, so it is
|
|
1346
|
+
* applied one at a time and coalesced: a pointer crossing the list faster than
|
|
1347
|
+
* a commit finishes skips the chains it passed over and settles on the one it
|
|
1348
|
+
* came to rest on, instead of working through a queue of hovers that have
|
|
1349
|
+
* already been left.
|
|
1350
|
+
*/
|
|
1351
|
+
function useChainHighlight({ disabled, hiddenChains, loadCount, pluginRef, selectedChains }) {
|
|
1352
|
+
const hoveredRef = (0, react.useRef)(null);
|
|
1353
|
+
/**
|
|
1354
|
+
* The chains left lit on the canvas as it stands. A freshly loaded structure
|
|
1355
|
+
* carries no dimming at all, which is what the empty key means.
|
|
1356
|
+
*/
|
|
1357
|
+
const shownRef = (0, react.useRef)(chainsKey(/* @__PURE__ */ new Set()));
|
|
1358
|
+
const committingRef = (0, react.useRef)(false);
|
|
1359
|
+
/**
|
|
1360
|
+
* Read when a commit comes round rather than closed over, so one already in
|
|
1361
|
+
* flight settles on the current props instead of the ones it started under.
|
|
1362
|
+
*/
|
|
1363
|
+
const propsRef = (0, react.useRef)({
|
|
1364
|
+
disabled,
|
|
1365
|
+
hiddenChains,
|
|
1366
|
+
selectedChains
|
|
1367
|
+
});
|
|
1368
|
+
propsRef.current = {
|
|
1369
|
+
disabled,
|
|
1370
|
+
hiddenChains,
|
|
1371
|
+
selectedChains
|
|
1372
|
+
};
|
|
1373
|
+
const litChains = (0, react.useCallback)(() => {
|
|
1374
|
+
const { disabled: hoverOff, hiddenChains: hidden, selectedChains: selected } = propsRef.current;
|
|
1375
|
+
const hovered = hoveredRef.current;
|
|
1376
|
+
if (!hoverOff && hovered !== null && !hidden.has(hovered)) return new Set([hovered]);
|
|
1377
|
+
return new Set([...selected].filter((chainId) => !hidden.has(chainId)));
|
|
1378
|
+
}, []);
|
|
1379
|
+
const settle = (0, react.useCallback)(_asyncToGenerator(function* () {
|
|
1380
|
+
if (committingRef.current) return;
|
|
1381
|
+
committingRef.current = true;
|
|
1382
|
+
try {
|
|
1383
|
+
while (pluginRef.current) {
|
|
1384
|
+
const lit = litChains();
|
|
1385
|
+
const key = chainsKey(lit);
|
|
1386
|
+
if (key === shownRef.current) break;
|
|
1387
|
+
if (!(yield dimChainsOutside(pluginRef.current, lit))) break;
|
|
1388
|
+
shownRef.current = key;
|
|
1457
1389
|
}
|
|
1458
|
-
|
|
1390
|
+
} catch (error) {
|
|
1391
|
+
console.error("Failed to dim chains:", error);
|
|
1392
|
+
} finally {
|
|
1393
|
+
committingRef.current = false;
|
|
1459
1394
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1395
|
+
}), [litChains, pluginRef]);
|
|
1396
|
+
const highlightChain = (0, react.useCallback)((chainId) => {
|
|
1397
|
+
hoveredRef.current = chainId;
|
|
1398
|
+
settle();
|
|
1399
|
+
}, [settle]);
|
|
1400
|
+
(0, react.useEffect)(() => {
|
|
1401
|
+
settle();
|
|
1402
|
+
}, [
|
|
1403
|
+
disabled,
|
|
1404
|
+
hiddenChains,
|
|
1405
|
+
selectedChains,
|
|
1406
|
+
settle
|
|
1407
|
+
]);
|
|
1408
|
+
(0, react.useEffect)(() => {
|
|
1409
|
+
shownRef.current = chainsKey(/* @__PURE__ */ new Set());
|
|
1410
|
+
settle();
|
|
1411
|
+
}, [loadCount, settle]);
|
|
1477
1412
|
return highlightChain;
|
|
1478
1413
|
}
|
|
1479
1414
|
//#endregion
|
|
@@ -2174,13 +2109,166 @@ function useResidueMarkers({ containerRef, residueColors, sequenceWrapper }) {
|
|
|
2174
2109
|
});
|
|
2175
2110
|
}
|
|
2176
2111
|
//#endregion
|
|
2112
|
+
//#region src/core/ProteinStructureViewer/utils/selection.ts
|
|
2113
|
+
/**
|
|
2114
|
+
* A stable string for a selection, for comparing one against another.
|
|
2115
|
+
*
|
|
2116
|
+
* A consumer holding the selection in state hands back a new object on every
|
|
2117
|
+
* render, so identity says nothing about whether the selection changed. Order
|
|
2118
|
+
* is normalized too: `{ residues: [2, 1] }` and `{ residues: [1, 2] }` select
|
|
2119
|
+
* the same residues and must not read as a change.
|
|
2120
|
+
*/
|
|
2121
|
+
function selectionKey(selection) {
|
|
2122
|
+
var _selection$residues, _selection$chains;
|
|
2123
|
+
const residues = [...new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : [])].sort((a, b) => a - b);
|
|
2124
|
+
return `${[...new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : [])].sort().join(",")}|${residues.join(",")}`;
|
|
2125
|
+
}
|
|
2126
|
+
/** True when the selection names nothing at all. */
|
|
2127
|
+
function isEmptySelection(selection) {
|
|
2128
|
+
var _selection$residues2, _selection$chains2;
|
|
2129
|
+
if (!selection) return true;
|
|
2130
|
+
return !((_selection$residues2 = selection.residues) === null || _selection$residues2 === void 0 ? void 0 : _selection$residues2.length) && !((_selection$chains2 = selection.chains) === null || _selection$chains2 === void 0 ? void 0 : _selection$chains2.length);
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* The residues a selection covers, ascending and without repeats.
|
|
2134
|
+
*
|
|
2135
|
+
* Chains are expanded through `residuesByChain`, which was read off the loaded
|
|
2136
|
+
* structure, so a chain contributes exactly the residues it holds rather than
|
|
2137
|
+
* the span between its ends. A residue named directly and also carried by a
|
|
2138
|
+
* selected chain is counted once.
|
|
2139
|
+
*/
|
|
2140
|
+
function selectionResidues(selection, residuesByChain) {
|
|
2141
|
+
var _selection$residues3, _selection$chains3;
|
|
2142
|
+
const residues = new Set((_selection$residues3 = selection.residues) !== null && _selection$residues3 !== void 0 ? _selection$residues3 : []);
|
|
2143
|
+
for (const chainId of (_selection$chains3 = selection.chains) !== null && _selection$chains3 !== void 0 ? _selection$chains3 : []) {
|
|
2144
|
+
var _residuesByChain$get;
|
|
2145
|
+
for (const index of (_residuesByChain$get = residuesByChain.get(chainId)) !== null && _residuesByChain$get !== void 0 ? _residuesByChain$get : []) residues.add(index);
|
|
2146
|
+
}
|
|
2147
|
+
return [...residues].sort((a, b) => a - b);
|
|
2148
|
+
}
|
|
2149
|
+
/**
|
|
2150
|
+
* Names a selection for the legend's readout.
|
|
2151
|
+
*
|
|
2152
|
+
* A single chain is named as one, since "Chain A" says more than the number of
|
|
2153
|
+
* residues it happens to hold. Anything else is reported as a count, which is
|
|
2154
|
+
* the only thing true of every shape a selection can take. A lone residue is
|
|
2155
|
+
* named by the caller instead, which reads it back off the structure to get its
|
|
2156
|
+
* three-letter code and the number the file gives it.
|
|
2157
|
+
*/
|
|
2158
|
+
function selectionLabel(selection, residueCount) {
|
|
2159
|
+
var _selection$chains4, _selection$residues4;
|
|
2160
|
+
const chains = (_selection$chains4 = selection.chains) !== null && _selection$chains4 !== void 0 ? _selection$chains4 : [];
|
|
2161
|
+
if (chains.length === 1 && !((_selection$residues4 = selection.residues) === null || _selection$residues4 === void 0 ? void 0 : _selection$residues4.length)) return `Chain ${chains[0]}`;
|
|
2162
|
+
return `${residueCount} residues`;
|
|
2163
|
+
}
|
|
2164
|
+
/** The mean of the values `read` returns, ignoring residues without one. */
|
|
2165
|
+
function meanOverResidues(residues, read) {
|
|
2166
|
+
let sum = 0;
|
|
2167
|
+
let count = 0;
|
|
2168
|
+
for (const index of residues) {
|
|
2169
|
+
const value = read(index);
|
|
2170
|
+
if (value === null || value === void 0) continue;
|
|
2171
|
+
sum += value;
|
|
2172
|
+
count++;
|
|
2173
|
+
}
|
|
2174
|
+
return count === 0 ? null : sum / count;
|
|
2175
|
+
}
|
|
2176
|
+
//#endregion
|
|
2177
|
+
//#region src/core/ProteinStructureViewer/utils/residueLoci.ts
|
|
2178
|
+
/**
|
|
2179
|
+
* Every atom the predicate accepts, as geometry Mol* can focus, frame and mark,
|
|
2180
|
+
* or undefined when it accepts none.
|
|
2181
|
+
*
|
|
2182
|
+
* One pass over the structure rather than a pass per residue: a whole-chain
|
|
2183
|
+
* selection names hundreds of residues, and resolving them one at a time would
|
|
2184
|
+
* walk the structure once for each.
|
|
2185
|
+
*
|
|
2186
|
+
* Atoms are bucketed by the unit they belong to because element indices are
|
|
2187
|
+
* unit-local. That bucketing is also what lets a result span a chain break: it
|
|
2188
|
+
* carries an entry per unit, which is what a range across two chains needs and
|
|
2189
|
+
* what a single unit-local entry cannot express.
|
|
2190
|
+
*
|
|
2191
|
+
* Whole residues are collected rather than one atom each, since the caller
|
|
2192
|
+
* frames the camera on the loci's bounding sphere and a lone atom would zoom
|
|
2193
|
+
* far closer than the residue warrants.
|
|
2194
|
+
*/
|
|
2195
|
+
function lociMatching(structure, matches) {
|
|
2196
|
+
const location = molstar_lib_mol_model_structure.StructureElement.Location.create(structure);
|
|
2197
|
+
const elements = [];
|
|
2198
|
+
for (const unit of structure.units) {
|
|
2199
|
+
location.unit = unit;
|
|
2200
|
+
const indices = [];
|
|
2201
|
+
for (let i = 0; i < unit.elements.length; i++) {
|
|
2202
|
+
location.element = unit.elements[i];
|
|
2203
|
+
if (matches(location)) indices.push(i);
|
|
2204
|
+
}
|
|
2205
|
+
if (indices.length > 0) elements.push({
|
|
2206
|
+
indices,
|
|
2207
|
+
unit
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
if (elements.length === 0) return void 0;
|
|
2211
|
+
return molstar_lib_mol_model_structure.StructureElement.Loci(structure, elements.map(({ indices, unit }) => ({
|
|
2212
|
+
indices: molstar_lib_mol_data_int.OrderedSet.ofSortedArray(indices),
|
|
2213
|
+
unit
|
|
2214
|
+
})));
|
|
2215
|
+
}
|
|
2216
|
+
/**
|
|
2217
|
+
* Every atom of the residues a selection covers, within one structure.
|
|
2218
|
+
*
|
|
2219
|
+
* `hiddenChains` is subtracted from the result. A hidden chain is not drawn, so
|
|
2220
|
+
* geometry built from it would have nothing to sit on: Mol*'s focus
|
|
2221
|
+
* representation draws its own ball-and-stick from whatever is focused, in a
|
|
2222
|
+
* part of the state tree the chain's own visibility does not reach, and a
|
|
2223
|
+
* hidden chain left in the loci would keep showing up there after its cartoon
|
|
2224
|
+
* had gone.
|
|
2225
|
+
*/
|
|
2226
|
+
function lociForSelectionInStructure(structure, selection, hiddenChains) {
|
|
2227
|
+
var _selection$residues, _selection$chains;
|
|
2228
|
+
if (isEmptySelection(selection)) return void 0;
|
|
2229
|
+
const residues = new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : []);
|
|
2230
|
+
const chains = new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : []);
|
|
2231
|
+
return lociMatching(structure, (location) => {
|
|
2232
|
+
const chainId = molstar_lib_mol_model_structure.StructureProperties.chain.auth_asym_id(location);
|
|
2233
|
+
if (hiddenChains === null || hiddenChains === void 0 ? void 0 : hiddenChains.has(chainId)) return false;
|
|
2234
|
+
return residues.has(molstar_lib_mol_model_structure.StructureProperties.residue.key(location)) || chains.has(chainId);
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Every atom of the residues between two 0-based indices, inclusive.
|
|
2239
|
+
*
|
|
2240
|
+
* A contiguous span is tested by comparison rather than by expanding it into a
|
|
2241
|
+
* set of indices: this runs on every residue a drag crosses, and a drag can
|
|
2242
|
+
* span the whole structure.
|
|
2243
|
+
*/
|
|
2244
|
+
function lociForResidueRange(structure, min, max) {
|
|
2245
|
+
return lociMatching(structure, (location) => {
|
|
2246
|
+
const index = molstar_lib_mol_model_structure.StructureProperties.residue.key(location);
|
|
2247
|
+
return index >= min && index <= max;
|
|
2248
|
+
});
|
|
2249
|
+
}
|
|
2250
|
+
/**
|
|
2251
|
+
* The same, across every structure loaded into a plugin. Results are merged
|
|
2252
|
+
* into the first structure that matched: a loci belongs to one structure, and
|
|
2253
|
+
* the residue index space already spans them as though they were one.
|
|
2254
|
+
*/
|
|
2255
|
+
function lociForSelection(plugin, selection, hiddenChains) {
|
|
2256
|
+
for (const entry of plugin.managers.structure.hierarchy.current.structures) {
|
|
2257
|
+
var _entry$cell$obj;
|
|
2258
|
+
const structure = (_entry$cell$obj = entry.cell.obj) === null || _entry$cell$obj === void 0 ? void 0 : _entry$cell$obj.data;
|
|
2259
|
+
if (!structure) continue;
|
|
2260
|
+
const loci = lociForSelectionInStructure(structure, selection, hiddenChains);
|
|
2261
|
+
if (loci) return loci;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
//#endregion
|
|
2177
2265
|
//#region src/core/ProteinStructureViewer/utils/residueRef.ts
|
|
2178
2266
|
/**
|
|
2179
2267
|
* Reads the residue a loci points at, or null when it points at nothing the
|
|
2180
2268
|
* viewer can address.
|
|
2181
2269
|
*
|
|
2182
2270
|
* `index` comes from Mol*'s residue key, which counts residues in file order
|
|
2183
|
-
* from zero and is the same counter `
|
|
2271
|
+
* from zero and is the same counter `injectPlddt` walks. Deriving it
|
|
2184
2272
|
* from the residue's *number* instead would tie it to how the file happens to
|
|
2185
2273
|
* be numbered: a crop beginning at residue 200 would index `plddt` at 199, and
|
|
2186
2274
|
* a file numbering each chain from 1 would give two residues the same index.
|
|
@@ -2964,32 +3052,6 @@ function neutralResidueColor(mode) {
|
|
|
2964
3052
|
return molstar_lib_mol_util_color.Color.fromRgb(g, g, g);
|
|
2965
3053
|
}
|
|
2966
3054
|
//#endregion
|
|
2967
|
-
//#region \0@oxc-project+runtime@0.134.0/helpers/esm/asyncToGenerator.js
|
|
2968
|
-
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
2969
|
-
try {
|
|
2970
|
-
var i = n[a](c), u = i.value;
|
|
2971
|
-
} catch (n) {
|
|
2972
|
-
e(n);
|
|
2973
|
-
return;
|
|
2974
|
-
}
|
|
2975
|
-
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
2976
|
-
}
|
|
2977
|
-
function _asyncToGenerator(n) {
|
|
2978
|
-
return function() {
|
|
2979
|
-
var t = this, e = arguments;
|
|
2980
|
-
return new Promise(function(r, o) {
|
|
2981
|
-
var a = n.apply(t, e);
|
|
2982
|
-
function _next(n) {
|
|
2983
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
2984
|
-
}
|
|
2985
|
-
function _throw(n) {
|
|
2986
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
2987
|
-
}
|
|
2988
|
-
_next(void 0);
|
|
2989
|
-
});
|
|
2990
|
-
};
|
|
2991
|
-
}
|
|
2992
|
-
//#endregion
|
|
2993
3055
|
//#region src/core/ProteinStructureViewer/utils/screenshot.ts
|
|
2994
3056
|
/**
|
|
2995
3057
|
* Each resolution as the Mol* preset that produces it: 1280x720, 1920x1080,
|
|
@@ -3328,6 +3390,25 @@ function focusResidue(plugin, loci) {
|
|
|
3328
3390
|
return clipRatio;
|
|
3329
3391
|
}
|
|
3330
3392
|
/**
|
|
3393
|
+
* Frames the camera on a loci without cropping anything around it.
|
|
3394
|
+
*
|
|
3395
|
+
* The counterpart to `focusResidue` for something meant to be read in context
|
|
3396
|
+
* rather than peeled down to: the camera fits what was selected, and the depth
|
|
3397
|
+
* clip is left open to the whole scene, so the rest of the structure stays on
|
|
3398
|
+
* screen. A selected chain is shown by dimming the chains around it, and they
|
|
3399
|
+
* have to be there to be dimmed.
|
|
3400
|
+
*/
|
|
3401
|
+
function frameLoci(plugin, loci) {
|
|
3402
|
+
var _plugin$canvas3d2;
|
|
3403
|
+
const camera = (_plugin$canvas3d2 = plugin.canvas3d) === null || _plugin$canvas3d2 === void 0 ? void 0 : _plugin$canvas3d2.camera;
|
|
3404
|
+
if (!camera) return;
|
|
3405
|
+
const sphere = molstar_lib_mol_model_loci.Loci.getBoundingSphere(loci);
|
|
3406
|
+
if (!sphere) return;
|
|
3407
|
+
const framingRadius = Math.max(sphere.radius + FOCUS_EXTRA_RADIUS, FOCUS_MIN_RADIUS);
|
|
3408
|
+
const snapshot = camera.getFocus(sphere.center, framingRadius);
|
|
3409
|
+
camera.setState(_objectSpread2(_objectSpread2({}, snapshot), {}, { radius: camera.state.radiusMax }), FOCUS_DURATION_MS);
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3331
3412
|
* Keeps the depth clip in sync with the zoom while a residue is focused.
|
|
3332
3413
|
*
|
|
3333
3414
|
* Mol*'s trackball zoom moves the camera without touching the clip radius, so
|
|
@@ -3337,15 +3418,15 @@ function focusResidue(plugin, loci) {
|
|
|
3337
3418
|
* camera is mid-transition, so it does not fight the focus animation.
|
|
3338
3419
|
*/
|
|
3339
3420
|
function syncClipToZoom(plugin, getClipRatio) {
|
|
3340
|
-
var _plugin$
|
|
3341
|
-
const camera = (_plugin$
|
|
3421
|
+
var _plugin$canvas3d3;
|
|
3422
|
+
const camera = (_plugin$canvas3d3 = plugin.canvas3d) === null || _plugin$canvas3d3 === void 0 ? void 0 : _plugin$canvas3d3.camera;
|
|
3342
3423
|
const clipRatio = getClipRatio();
|
|
3343
3424
|
if (!camera || clipRatio === null || camera.transition.inTransition) return;
|
|
3344
3425
|
const radius = clipRadiusForDistance(molstar_lib_mol_math_linear_algebra.Vec3.distance(camera.state.position, camera.state.target), clipRatio, camera.state.radiusMax);
|
|
3345
3426
|
if (Math.abs(camera.state.radius - radius) > CLIP_EPSILON) {
|
|
3346
|
-
var _plugin$
|
|
3427
|
+
var _plugin$canvas3d4;
|
|
3347
3428
|
camera.setState({ radius });
|
|
3348
|
-
(_plugin$
|
|
3429
|
+
(_plugin$canvas3d4 = plugin.canvas3d) === null || _plugin$canvas3d4 === void 0 || _plugin$canvas3d4.requestDraw();
|
|
3349
3430
|
}
|
|
3350
3431
|
}
|
|
3351
3432
|
//#endregion
|
|
@@ -3560,6 +3641,22 @@ function mergeMolstarSpec(base, override) {
|
|
|
3560
3641
|
return merged;
|
|
3561
3642
|
}
|
|
3562
3643
|
//#endregion
|
|
3644
|
+
//#region src/core/ProteinStructureViewer/utils/structureFormat.ts
|
|
3645
|
+
/** How much of the file to inspect when guessing the format. */
|
|
3646
|
+
const DETECT_SAMPLE = 16384;
|
|
3647
|
+
/**
|
|
3648
|
+
* Infers whether `text` is PDB or mmCIF (PDBx).
|
|
3649
|
+
*
|
|
3650
|
+
* mmCIF documents start with a `data_` block and name their coordinates
|
|
3651
|
+
* `_atom_site.`. PDB never does either, so either marker is enough. A leading
|
|
3652
|
+
* comment (`# ...`) before `data_` is still mmCIF.
|
|
3653
|
+
*/
|
|
3654
|
+
function detectStructureFormat(text) {
|
|
3655
|
+
const sample = text.slice(0, DETECT_SAMPLE);
|
|
3656
|
+
if (/(?:^|\n)\s*data_/.test(sample) || /_atom_site\./.test(sample)) return "mmcif";
|
|
3657
|
+
return "pdb";
|
|
3658
|
+
}
|
|
3659
|
+
//#endregion
|
|
3563
3660
|
//#region src/core/ProteinStructureViewer/utils/chainColorTheme.ts
|
|
3564
3661
|
/** Mol* color theme name under which per-chain coloring is registered. */
|
|
3565
3662
|
const CHAIN_COLOR_THEME_NAME = "chain-color";
|
|
@@ -3698,9 +3795,183 @@ function injectPlddtIntoPdb(pdbData, plddtValues) {
|
|
|
3698
3795
|
return paddedLine.substring(0, 60) + bfactorStr + paddedLine.substring(66);
|
|
3699
3796
|
}).join("\n");
|
|
3700
3797
|
}
|
|
3798
|
+
const ATOM_SITE_PREFIX = "_atom_site.";
|
|
3799
|
+
const B_FACTOR_COLUMN = "B_iso_or_equiv";
|
|
3800
|
+
function skipCifWhitespace(line, index) {
|
|
3801
|
+
let i = index;
|
|
3802
|
+
while (i < line.length && /\s/.test(line.charAt(i))) i += 1;
|
|
3803
|
+
return i;
|
|
3804
|
+
}
|
|
3805
|
+
function readCifToken(line, start) {
|
|
3806
|
+
const quote = line.charAt(start);
|
|
3807
|
+
if (quote === "'" || quote === "\"") {
|
|
3808
|
+
const end = line.indexOf(quote, start + 1);
|
|
3809
|
+
if (end < 0) return {
|
|
3810
|
+
next: line.length,
|
|
3811
|
+
token: line.slice(start + 1)
|
|
3812
|
+
};
|
|
3813
|
+
return {
|
|
3814
|
+
next: end + 1,
|
|
3815
|
+
token: line.slice(start + 1, end)
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
let i = start;
|
|
3819
|
+
while (i < line.length && !/\s/.test(line.charAt(i))) i += 1;
|
|
3820
|
+
return {
|
|
3821
|
+
next: i,
|
|
3822
|
+
token: line.slice(start, i)
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Splits one CIF line into tokens, honouring `'quoted'` and `"quoted"` values.
|
|
3827
|
+
* Atom-site rows are one line each in practice; multiline `;text;` fields are
|
|
3828
|
+
* not used there and are not handled.
|
|
3829
|
+
*/
|
|
3830
|
+
function tokenizeCifLine(line) {
|
|
3831
|
+
const tokens = [];
|
|
3832
|
+
let i = skipCifWhitespace(line, 0);
|
|
3833
|
+
while (i < line.length) {
|
|
3834
|
+
const { next, token } = readCifToken(line, i);
|
|
3835
|
+
tokens.push(token);
|
|
3836
|
+
i = skipCifWhitespace(line, next);
|
|
3837
|
+
}
|
|
3838
|
+
return tokens;
|
|
3839
|
+
}
|
|
3840
|
+
function isCifNull(value) {
|
|
3841
|
+
return value === void 0 || value === "." || value === "?";
|
|
3842
|
+
}
|
|
3843
|
+
function columnIndex(columns, name) {
|
|
3844
|
+
return columns.indexOf(name);
|
|
3845
|
+
}
|
|
3846
|
+
/**
|
|
3847
|
+
* Residue identity matching `injectPlddtIntoPdb`: chain + sequence number +
|
|
3848
|
+
* insertion code. Prefers the `auth_*` columns Mol* reports on the parsed
|
|
3849
|
+
* model, falling back to `label_*` when a file omitted them. `.` / `?`
|
|
3850
|
+
* insertion codes count as empty, the way a blank PDB column 27 does.
|
|
3851
|
+
*/
|
|
3852
|
+
function mmcifResidueKey(tokens, columns) {
|
|
3853
|
+
var _ref, _ref2, _ref3, _ref4;
|
|
3854
|
+
const authAsym = columnIndex(columns, "auth_asym_id");
|
|
3855
|
+
const labelAsym = columnIndex(columns, "label_asym_id");
|
|
3856
|
+
const authSeq = columnIndex(columns, "auth_seq_id");
|
|
3857
|
+
const labelSeq = columnIndex(columns, "label_seq_id");
|
|
3858
|
+
const ins = columnIndex(columns, "pdbx_PDB_ins_code");
|
|
3859
|
+
return `${(_ref = (_ref2 = authAsym >= 0 ? tokens[authAsym] : void 0) !== null && _ref2 !== void 0 ? _ref2 : labelAsym >= 0 ? tokens[labelAsym] : void 0) !== null && _ref !== void 0 ? _ref : ""}|${(_ref3 = (_ref4 = authSeq >= 0 ? tokens[authSeq] : void 0) !== null && _ref4 !== void 0 ? _ref4 : labelSeq >= 0 ? tokens[labelSeq] : void 0) !== null && _ref3 !== void 0 ? _ref3 : ""}|${isCifNull(ins >= 0 ? tokens[ins] : void 0) ? "" : tokens[ins]}`;
|
|
3860
|
+
}
|
|
3861
|
+
function isAtomSiteRow(tokens, columns) {
|
|
3862
|
+
const group = columnIndex(columns, "group_PDB");
|
|
3863
|
+
if (group < 0) return true;
|
|
3864
|
+
const value = tokens[group];
|
|
3865
|
+
return value === "ATOM" || value === "HETATM";
|
|
3866
|
+
}
|
|
3867
|
+
function plddtBFactor(residueIndex, plddtValues) {
|
|
3868
|
+
var _plddtValues$residueI2;
|
|
3869
|
+
return (residueIndex < plddtValues.length ? ((_plddtValues$residueI2 = plddtValues[residueIndex]) !== null && _plddtValues$residueI2 !== void 0 ? _plddtValues$residueI2 : 0) * 100 : 50).toFixed(2);
|
|
3870
|
+
}
|
|
3871
|
+
function isLoopStart(line) {
|
|
3872
|
+
return line.trim() === "loop_" || line.trim().startsWith("loop_");
|
|
3873
|
+
}
|
|
3874
|
+
function isAtomSiteColumn(line) {
|
|
3875
|
+
return line.trim().startsWith(ATOM_SITE_PREFIX);
|
|
3876
|
+
}
|
|
3877
|
+
function endsAtomSiteLoop(line) {
|
|
3878
|
+
const trimmed = line.trim();
|
|
3879
|
+
if (trimmed === "") return false;
|
|
3880
|
+
if (trimmed.startsWith("#")) return true;
|
|
3881
|
+
if (isLoopStart(trimmed)) return true;
|
|
3882
|
+
if (trimmed.startsWith("data_") || trimmed.startsWith("save_")) return true;
|
|
3883
|
+
return trimmed.startsWith("_") && !trimmed.startsWith(ATOM_SITE_PREFIX);
|
|
3884
|
+
}
|
|
3885
|
+
function isAtomSiteLoop(lines, loopIndex) {
|
|
3886
|
+
let peek = loopIndex + 1;
|
|
3887
|
+
while (peek < lines.length && lines[peek].trim() === "") peek += 1;
|
|
3888
|
+
return peek < lines.length && isAtomSiteColumn(lines[peek]);
|
|
3889
|
+
}
|
|
3890
|
+
function ensureBFactorColumn(columns, headerLines) {
|
|
3891
|
+
const existing = columnIndex(columns, B_FACTOR_COLUMN);
|
|
3892
|
+
if (existing >= 0) return existing;
|
|
3893
|
+
columns.push(B_FACTOR_COLUMN);
|
|
3894
|
+
headerLines.push(`${ATOM_SITE_PREFIX}${B_FACTOR_COLUMN}`);
|
|
3895
|
+
return columns.length - 1;
|
|
3896
|
+
}
|
|
3897
|
+
function rewriteAtomSiteRow(dataLine, columns, bIndex, plddtValues, walk) {
|
|
3898
|
+
if (dataLine.trim() === "") return dataLine;
|
|
3899
|
+
const tokens = tokenizeCifLine(dataLine);
|
|
3900
|
+
if (tokens.length === 0) return dataLine;
|
|
3901
|
+
while (tokens.length < columns.length) tokens.push(".");
|
|
3902
|
+
if (isAtomSiteRow(tokens, columns)) {
|
|
3903
|
+
const residue = mmcifResidueKey(tokens, columns);
|
|
3904
|
+
if (residue !== walk.lastResidue) {
|
|
3905
|
+
walk.residueIndex += 1;
|
|
3906
|
+
walk.lastResidue = residue;
|
|
3907
|
+
}
|
|
3908
|
+
tokens[bIndex] = plddtBFactor(walk.residueIndex, plddtValues);
|
|
3909
|
+
}
|
|
3910
|
+
return tokens.join(" ");
|
|
3911
|
+
}
|
|
3912
|
+
function rewriteAtomSiteLoop(lines, loopIndex, plddtValues, walk) {
|
|
3913
|
+
const rows = [lines[loopIndex]];
|
|
3914
|
+
let i = loopIndex + 1;
|
|
3915
|
+
const columns = [];
|
|
3916
|
+
while (i < lines.length && isAtomSiteColumn(lines[i])) {
|
|
3917
|
+
columns.push(lines[i].trim().slice(11));
|
|
3918
|
+
rows.push(lines[i]);
|
|
3919
|
+
i += 1;
|
|
3920
|
+
}
|
|
3921
|
+
const bIndex = ensureBFactorColumn(columns, rows);
|
|
3922
|
+
while (i < lines.length && !endsAtomSiteLoop(lines[i])) {
|
|
3923
|
+
rows.push(rewriteAtomSiteRow(lines[i], columns, bIndex, plddtValues, walk));
|
|
3924
|
+
i += 1;
|
|
3925
|
+
}
|
|
3926
|
+
return {
|
|
3927
|
+
next: i,
|
|
3928
|
+
rows
|
|
3929
|
+
};
|
|
3930
|
+
}
|
|
3931
|
+
/**
|
|
3932
|
+
* Rewrites an mmCIF (PDBx) file's `_atom_site.B_iso_or_equiv` column with
|
|
3933
|
+
* pLDDT scores so Mol* can read them off the parsed model, the same way
|
|
3934
|
+
* `injectPlddtIntoPdb` rewrites the PDB B-factor column. Scores arrive on a
|
|
3935
|
+
* 0-1 scale and are stored on the conventional 0-100 one. Residues past the
|
|
3936
|
+
* end of `plddtValues` fall back to a mid-confidence 50.
|
|
3937
|
+
*
|
|
3938
|
+
* If the file has no B-factor column, one is appended to the atom-site loop
|
|
3939
|
+
* rather than leaving the scores behind.
|
|
3940
|
+
*/
|
|
3941
|
+
function injectPlddtIntoMmcif(mmcifData, plddtValues) {
|
|
3942
|
+
const lines = mmcifData.split("\n");
|
|
3943
|
+
const result = [];
|
|
3944
|
+
const walk = {
|
|
3945
|
+
lastResidue: "",
|
|
3946
|
+
residueIndex: -1
|
|
3947
|
+
};
|
|
3948
|
+
let i = 0;
|
|
3949
|
+
let rewrittenLoop = false;
|
|
3950
|
+
while (i < lines.length) {
|
|
3951
|
+
const line = lines[i];
|
|
3952
|
+
if (!rewrittenLoop && isLoopStart(line) && isAtomSiteLoop(lines, i)) {
|
|
3953
|
+
const rewritten = rewriteAtomSiteLoop(lines, i, plddtValues, walk);
|
|
3954
|
+
result.push(...rewritten.rows);
|
|
3955
|
+
i = rewritten.next;
|
|
3956
|
+
rewrittenLoop = true;
|
|
3957
|
+
continue;
|
|
3958
|
+
}
|
|
3959
|
+
result.push(line);
|
|
3960
|
+
i += 1;
|
|
3961
|
+
}
|
|
3962
|
+
return result.join("\n");
|
|
3963
|
+
}
|
|
3964
|
+
/**
|
|
3965
|
+
* Rewrites a structure file's B-factors with pLDDT scores. Dispatches to
|
|
3966
|
+
* `injectPlddtIntoPdb` or `injectPlddtIntoMmcif` from `format`, or from
|
|
3967
|
+
* detecting the format of `data` when `format` is omitted.
|
|
3968
|
+
*/
|
|
3969
|
+
function injectPlddt(data, plddtValues, format = detectStructureFormat(data)) {
|
|
3970
|
+
return format === "mmcif" ? injectPlddtIntoMmcif(data, plddtValues) : injectPlddtIntoPdb(data, plddtValues);
|
|
3971
|
+
}
|
|
3701
3972
|
/**
|
|
3702
3973
|
* Mol* color theme that reads pLDDT scores out of the B-factor column, paired
|
|
3703
|
-
* with `
|
|
3974
|
+
* with `injectPlddt`. Registered per plugin instance.
|
|
3704
3975
|
*/
|
|
3705
3976
|
const PlddtColoring = molstar_lib_mol_model_props_common_custom_element_property.CustomElementProperty.create({
|
|
3706
3977
|
coloring: {
|
|
@@ -4010,27 +4281,30 @@ const NOTHING_LOADED = {
|
|
|
4010
4281
|
residuesByChain: /* @__PURE__ */ new Map()
|
|
4011
4282
|
};
|
|
4012
4283
|
/**
|
|
4013
|
-
* Parses the
|
|
4284
|
+
* Parses the structure and draws each chain: a cartoon over its polymer, and
|
|
4014
4285
|
* ball-and-stick over its ligands and ions.
|
|
4015
4286
|
*
|
|
4016
4287
|
* Mol*'s `default` hierarchy preset would be shorter, but it groups every
|
|
4017
4288
|
* polymer chain into a single component, and a component is the unit Mol* can
|
|
4018
4289
|
* hide. Building them per chain is what makes a chain individually hideable;
|
|
4019
4290
|
* the refs handed back are what `setSubtreeVisibility` is later pointed at.
|
|
4291
|
+
*
|
|
4292
|
+
* The wrapper detects PDB vs mmCIF from the text; this just forwards that
|
|
4293
|
+
* guess to Mol*.
|
|
4020
4294
|
*/
|
|
4021
4295
|
function loadStructure(_x9, _x10, _x11, _x12) {
|
|
4022
4296
|
return _loadStructure.apply(this, arguments);
|
|
4023
4297
|
}
|
|
4024
4298
|
function _loadStructure() {
|
|
4025
|
-
_loadStructure = _asyncToGenerator(function* (plugin,
|
|
4299
|
+
_loadStructure = _asyncToGenerator(function* (plugin, structureText, usePlddtColoring, showAxes) {
|
|
4026
4300
|
try {
|
|
4027
4301
|
var _plugin$canvas3d;
|
|
4028
4302
|
yield plugin.clear();
|
|
4029
4303
|
const data = yield plugin.builders.data.rawData({
|
|
4030
|
-
data:
|
|
4304
|
+
data: structureText,
|
|
4031
4305
|
label: "Structure"
|
|
4032
4306
|
});
|
|
4033
|
-
const trajectory = yield plugin.builders.structure.parseTrajectory(data,
|
|
4307
|
+
const trajectory = yield plugin.builders.structure.parseTrajectory(data, detectStructureFormat(structureText));
|
|
4034
4308
|
const model = yield plugin.builders.structure.createModel(trajectory);
|
|
4035
4309
|
const structure = yield plugin.builders.structure.createStructure(model, {
|
|
4036
4310
|
name: "model",
|
|
@@ -4104,16 +4378,17 @@ function _applyColorTheme() {
|
|
|
4104
4378
|
*
|
|
4105
4379
|
* The plugin is built once and then mutated in place. Creating it is expensive
|
|
4106
4380
|
* and destroys the camera, so prop changes that Mol* can absorb (background,
|
|
4107
|
-
* axes, coloring, new
|
|
4108
|
-
* sibling hooks rather than by rebuilding.
|
|
4381
|
+
* axes, coloring, new structure) are pushed in through the effects below and in
|
|
4382
|
+
* the sibling hooks rather than by rebuilding.
|
|
4109
4383
|
*/
|
|
4110
|
-
function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download, edgeColor, hasPlddt, hiddenChains, highlightColor, mode, molstarSpec, onChainHover, onChainSelect, onChainToggle, onResidueClick, onResidueHover, onSelectionChange, onSelectionClear,
|
|
4384
|
+
function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download, edgeColor, hasPlddt, hiddenChains, highlightColor, mode, molstarSpec, onChainHover, onChainSelect, onChainToggle, onResidueClick, onResidueHover, onSelectionChange, onSelectionClear, selectedChains, sequenceViewerBackgroundColor, showAxes, showSequenceViewer, structure }) {
|
|
4111
4385
|
const pluginRef = (0, react.useRef)(null);
|
|
4112
4386
|
const residueValueThemeRef = (0, react.useRef)(null);
|
|
4113
4387
|
const chainColorThemeRef = (0, react.useRef)(null);
|
|
4114
|
-
const
|
|
4388
|
+
const currentStructureRef = (0, react.useRef)(null);
|
|
4115
4389
|
const [isReady, setIsReady] = (0, react.useState)(false);
|
|
4116
4390
|
const [chains, setChains] = (0, react.useState)([]);
|
|
4391
|
+
const [loadCount, setLoadCount] = (0, react.useState)(0);
|
|
4117
4392
|
/**
|
|
4118
4393
|
* Where each chain's components live in the state tree, which is what the
|
|
4119
4394
|
* visibility effect points `setSubtreeVisibility` at. Rewritten by every
|
|
@@ -4170,6 +4445,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4170
4445
|
residuesByChainRef.current = loaded.residuesByChain;
|
|
4171
4446
|
applyChainVisibility(plugin, loaded.componentRefs, hiddenChainsRef.current);
|
|
4172
4447
|
setChains((prev) => chainsEqual(prev, loaded.chains) ? prev : loaded.chains);
|
|
4448
|
+
setLoadCount((count) => count + 1);
|
|
4173
4449
|
}, []);
|
|
4174
4450
|
const onResidueClickRef = (0, react.useRef)(onResidueClick);
|
|
4175
4451
|
onResidueClickRef.current = onResidueClick;
|
|
@@ -4188,9 +4464,9 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4188
4464
|
highlightColor,
|
|
4189
4465
|
mode,
|
|
4190
4466
|
molstarSpec,
|
|
4191
|
-
pdb,
|
|
4192
4467
|
showAxes,
|
|
4193
|
-
showSequenceViewer
|
|
4468
|
+
showSequenceViewer,
|
|
4469
|
+
structure
|
|
4194
4470
|
});
|
|
4195
4471
|
initialPropsRef.current = {
|
|
4196
4472
|
backgroundColor,
|
|
@@ -4199,9 +4475,9 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4199
4475
|
highlightColor,
|
|
4200
4476
|
mode,
|
|
4201
4477
|
molstarSpec,
|
|
4202
|
-
pdb,
|
|
4203
4478
|
showAxes,
|
|
4204
|
-
showSequenceViewer
|
|
4479
|
+
showSequenceViewer,
|
|
4480
|
+
structure
|
|
4205
4481
|
};
|
|
4206
4482
|
/**
|
|
4207
4483
|
* The consumer's `canvas3d` overrides, keyed by content. Unlike the rest of
|
|
@@ -4256,8 +4532,8 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4256
4532
|
residueValueThemeRef.current = residueValueTheme;
|
|
4257
4533
|
chainColorThemeRef.current = chainColorTheme;
|
|
4258
4534
|
const latest = initialPropsRef.current;
|
|
4259
|
-
const loaded = yield loadStructure(plugin, latest.
|
|
4260
|
-
|
|
4535
|
+
const loaded = yield loadStructure(plugin, latest.structure, latest.hasPlddt, latest.showAxes);
|
|
4536
|
+
currentStructureRef.current = latest.structure;
|
|
4261
4537
|
adoptLoadedStructure(plugin, loaded);
|
|
4262
4538
|
/**
|
|
4263
4539
|
* Mol*'s click behavior is a BehaviorSubject, so subscribing replays
|
|
@@ -4319,7 +4595,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4319
4595
|
chainColorThemeRef.current = null;
|
|
4320
4596
|
componentRefsRef.current = /* @__PURE__ */ new Map();
|
|
4321
4597
|
residuesByChainRef.current = /* @__PURE__ */ new Map();
|
|
4322
|
-
|
|
4598
|
+
currentStructureRef.current = null;
|
|
4323
4599
|
clipRatioRef.current = null;
|
|
4324
4600
|
setIsReady(false);
|
|
4325
4601
|
};
|
|
@@ -4378,16 +4654,16 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4378
4654
|
(0, react.useEffect)(() => {
|
|
4379
4655
|
const plugin = pluginRef.current;
|
|
4380
4656
|
if (!plugin || !isReady) return;
|
|
4381
|
-
if (
|
|
4382
|
-
|
|
4657
|
+
if (structure === currentStructureRef.current) return;
|
|
4658
|
+
currentStructureRef.current = structure;
|
|
4383
4659
|
clipRatioRef.current = null;
|
|
4384
4660
|
lastHoverRef.current = null;
|
|
4385
|
-
loadStructure(plugin,
|
|
4661
|
+
loadStructure(plugin, structure, hasPlddt, showAxes).then((loaded) => {
|
|
4386
4662
|
if (pluginRef.current !== plugin) return;
|
|
4387
4663
|
adoptLoadedStructure(plugin, loaded);
|
|
4388
4664
|
});
|
|
4389
4665
|
}, [
|
|
4390
|
-
|
|
4666
|
+
structure,
|
|
4391
4667
|
hasPlddt,
|
|
4392
4668
|
isReady,
|
|
4393
4669
|
adoptLoadedStructure
|
|
@@ -4401,6 +4677,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4401
4677
|
chainColorThemeRef,
|
|
4402
4678
|
chains,
|
|
4403
4679
|
isReady,
|
|
4680
|
+
loadCount,
|
|
4404
4681
|
pluginRef,
|
|
4405
4682
|
residuesByChainRef,
|
|
4406
4683
|
residueValueThemeRef,
|
|
@@ -4516,10 +4793,14 @@ function focusWithShell(plugin, loci, includeNeighbours, currentShell, pluginRef
|
|
|
4516
4793
|
/**
|
|
4517
4794
|
* Makes `selection` the one thing that decides where the camera sits.
|
|
4518
4795
|
*
|
|
4519
|
-
* Setting the prop frames whatever it covers
|
|
4520
|
-
*
|
|
4521
|
-
*
|
|
4522
|
-
*
|
|
4796
|
+
* Setting the prop frames whatever it covers; clearing it drops the focus and
|
|
4797
|
+
* zooms back out. The camera frames the selection's bounding sphere, so a range
|
|
4798
|
+
* or a whole chain is fitted rather than approached as a point - one residue is
|
|
4799
|
+
* just the smallest case of that.
|
|
4800
|
+
*
|
|
4801
|
+
* Residues are also marked, which is what draws Mol*'s ball-and-stick and
|
|
4802
|
+
* outline over them. Whole chains are not: those are shown by dimming the
|
|
4803
|
+
* chains around them instead, in `useChainHighlight`.
|
|
4523
4804
|
*
|
|
4524
4805
|
* The click subscription only reports the selection it was given, so a click
|
|
4525
4806
|
* moves the camera only once the consumer accepts it and echoes it back -
|
|
@@ -4528,6 +4809,7 @@ function focusWithShell(plugin, loci, includeNeighbours, currentShell, pluginRef
|
|
|
4528
4809
|
* responds to a click whether or not the consumer wires the prop up.
|
|
4529
4810
|
*/
|
|
4530
4811
|
function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setClipRatio }) {
|
|
4812
|
+
var _selection$chains, _selection$residues;
|
|
4531
4813
|
/**
|
|
4532
4814
|
* What this hook last pointed the camera at, as a key rather than the object
|
|
4533
4815
|
* itself: a consumer echoing a selection back builds a new object each time,
|
|
@@ -4552,6 +4834,11 @@ function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setCli
|
|
|
4552
4834
|
* echoing an equal selection back from refocusing on every render.
|
|
4553
4835
|
*/
|
|
4554
4836
|
const focusKey = selection ? `${selectionKey(selection)}#${[...hiddenChains].sort().join(",")}` : null;
|
|
4837
|
+
/**
|
|
4838
|
+
* Whether the selection names chains and nothing else, which is the shape
|
|
4839
|
+
* shown by dimming the chains around it rather than by focusing it.
|
|
4840
|
+
*/
|
|
4841
|
+
const chainsOnly = Boolean((selection === null || selection === void 0 || (_selection$chains = selection.chains) === null || _selection$chains === void 0 ? void 0 : _selection$chains.length) && !((_selection$residues = selection.residues) === null || _selection$residues === void 0 ? void 0 : _selection$residues.length));
|
|
4555
4842
|
(0, react.useEffect)(() => {
|
|
4556
4843
|
var _chosen$chains;
|
|
4557
4844
|
const plugin = pluginRef.current;
|
|
@@ -4572,6 +4859,12 @@ function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setCli
|
|
|
4572
4859
|
clearFocus(plugin, setClipRatio, false);
|
|
4573
4860
|
return;
|
|
4574
4861
|
}
|
|
4862
|
+
if (chainsOnly) {
|
|
4863
|
+
plugin.managers.structure.focus.clear();
|
|
4864
|
+
frameLoci(plugin, loci);
|
|
4865
|
+
setClipRatio(null);
|
|
4866
|
+
return;
|
|
4867
|
+
}
|
|
4575
4868
|
return focusWithShell(plugin, loci, !((_chosen$chains = chosen.chains) === null || _chosen$chains === void 0 ? void 0 : _chosen$chains.length), neighbourhoodRef, pluginRef, setClipRatio);
|
|
4576
4869
|
}, [
|
|
4577
4870
|
focusKey,
|
|
@@ -4733,7 +5026,7 @@ const _excluded$1 = [
|
|
|
4733
5026
|
"onResidueClick",
|
|
4734
5027
|
"onResidueHover",
|
|
4735
5028
|
"onSelectionChange",
|
|
4736
|
-
"
|
|
5029
|
+
"structure",
|
|
4737
5030
|
"plddt",
|
|
4738
5031
|
"residueOverlay",
|
|
4739
5032
|
"selection",
|
|
@@ -4810,14 +5103,14 @@ function resolveScaleProps(overlay, hasPlddt) {
|
|
|
4810
5103
|
*/
|
|
4811
5104
|
const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
4812
5105
|
var _selection$chains;
|
|
4813
|
-
const { backgroundColor, chainColors: chainColorOverrides, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, onChainVisibilityChange, onChainsChange, molstarSpec, onResidueClick, onResidueHover, onSelectionChange,
|
|
5106
|
+
const { backgroundColor, chainColors: chainColorOverrides, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, onChainVisibilityChange, onChainsChange, molstarSpec, onResidueClick, onResidueHover, onSelectionChange, structure, plddt, residueOverlay, selection = null, sequenceViewerBackgroundColor, showAxes = true, showChainLegend = true, showLegend = true, showSequenceViewer = true, stats } = props, rest = _objectWithoutProperties(props, _excluded$1);
|
|
4814
5107
|
const theme = (0, _mui_material_styles.useTheme)();
|
|
4815
5108
|
const mode = (0, _czi_sds_components.getMode)({ theme });
|
|
4816
5109
|
const semanticColors = (0, _czi_sds_components.getSemanticColors)({ theme });
|
|
4817
5110
|
const pluginMountRef = (0, react.useRef)(null);
|
|
4818
5111
|
const hasPlddt = Boolean(plddt && plddt.length > 0);
|
|
4819
|
-
const
|
|
4820
|
-
|
|
5112
|
+
const processedStructure = (0, react.useMemo)(() => hasPlddt ? injectPlddt(structure, plddt) : structure, [
|
|
5113
|
+
structure,
|
|
4821
5114
|
plddt,
|
|
4822
5115
|
hasPlddt
|
|
4823
5116
|
]);
|
|
@@ -4864,13 +5157,14 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4864
5157
|
onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(isOnlyThisChain ? null : { chains: [chainId] });
|
|
4865
5158
|
}, [onSelectionChange, selection]);
|
|
4866
5159
|
/**
|
|
4867
|
-
*
|
|
4868
|
-
* sequence panel's captions
|
|
5160
|
+
* Dims every other chain while a chain's name is pointed at, in the legend
|
|
5161
|
+
* or in the sequence panel's captions, and leaves the selected chain lit
|
|
5162
|
+
* once the pointer has gone.
|
|
4869
5163
|
*
|
|
4870
|
-
* Reached through a ref because the two ends need each other:
|
|
4871
|
-
*
|
|
4872
|
-
* has to be handed the callback to give the captions it renders. This
|
|
4873
|
-
* is stable and forwards to whatever the highlight hook installs once
|
|
5164
|
+
* Reached through a ref because the two ends need each other: dimming
|
|
5165
|
+
* other chains takes the plugin, which the hook below creates, and that
|
|
5166
|
+
* hook has to be handed the callback to give the captions it renders. This
|
|
5167
|
+
* one is stable and forwards to whatever the highlight hook installs once
|
|
4874
5168
|
* there is a plugin for it to talk to.
|
|
4875
5169
|
*/
|
|
4876
5170
|
const highlightChainRef = (0, react.useRef)(() => void 0);
|
|
@@ -4884,7 +5178,7 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4884
5178
|
hiddenChains: hiddenChainsProp,
|
|
4885
5179
|
onChainVisibilityChange
|
|
4886
5180
|
});
|
|
4887
|
-
const { chainColorThemeRef, chains: loadedChains, isReady, pluginRef, residuesByChainRef, residueValueThemeRef, setClipRatio } = useMolstarPlugin({
|
|
5181
|
+
const { chainColorThemeRef, chains: loadedChains, isReady, loadCount, pluginRef, residuesByChainRef, residueValueThemeRef, setClipRatio } = useMolstarPlugin({
|
|
4888
5182
|
backgroundColor: bgColor,
|
|
4889
5183
|
chainColors,
|
|
4890
5184
|
containerRef: pluginMountRef,
|
|
@@ -4902,15 +5196,18 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4902
5196
|
onResidueHover: handleResidueHover,
|
|
4903
5197
|
onSelectionChange,
|
|
4904
5198
|
onSelectionClear: handleSelectionClear,
|
|
4905
|
-
pdb: processedPdb,
|
|
4906
5199
|
selectedChains,
|
|
4907
5200
|
sequenceViewerBackgroundColor,
|
|
4908
5201
|
showAxes,
|
|
4909
|
-
showSequenceViewer
|
|
5202
|
+
showSequenceViewer,
|
|
5203
|
+
structure: processedStructure
|
|
4910
5204
|
});
|
|
4911
5205
|
highlightChainRef.current = useChainHighlight({
|
|
4912
5206
|
disabled: disableChainHighlightOnHover,
|
|
4913
|
-
|
|
5207
|
+
hiddenChains,
|
|
5208
|
+
loadCount,
|
|
5209
|
+
pluginRef,
|
|
5210
|
+
selectedChains
|
|
4914
5211
|
});
|
|
4915
5212
|
(0, react.useEffect)(() => {
|
|
4916
5213
|
setChains(loadedChains);
|
|
@@ -5462,6 +5759,9 @@ exports.PLASMA_COLOR_SCALE = PLASMA_COLOR_SCALE;
|
|
|
5462
5759
|
exports.PLDDT_COLOR_SCALE = PLDDT_COLOR_SCALE;
|
|
5463
5760
|
exports.ProteinStructureViewer = ProteinStructureViewer_default;
|
|
5464
5761
|
exports.StackedBarChart = StackedBarChart;
|
|
5762
|
+
exports.detectStructureFormat = detectStructureFormat;
|
|
5763
|
+
exports.injectPlddt = injectPlddt;
|
|
5764
|
+
exports.injectPlddtIntoMmcif = injectPlddtIntoMmcif;
|
|
5465
5765
|
exports.injectPlddtIntoPdb = injectPlddtIntoPdb;
|
|
5466
5766
|
exports.sampleColorScale = sampleColorScale;
|
|
5467
5767
|
exports.toCssGradient = toCssGradient;
|