@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.esm.js
CHANGED
|
@@ -7,9 +7,8 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
import { Button, Legend, Theme, Tooltip, TooltipTable, defaultTheme, fontBodyXs, fontBodyXxxs, fontCodeXs, fontHeaderM, fontHeaderS, fontTabularSemiboldXxxxs, generateDiscreteColors, getCorners, getMode, getSemanticColors, getSpaces } from "@czi-sds/components";
|
|
8
8
|
import { ThemeProvider, useTheme } from "@mui/material/styles";
|
|
9
9
|
import { ApertureIcon, ArrowCounterClockwiseIcon, CheckIcon, CopyIcon, EyeIcon, EyeSlashIcon, QuestionIcon } from "@phosphor-icons/react";
|
|
10
|
-
import { Color } from "molstar/lib/mol-util/color";
|
|
11
|
-
import { OrderedSet } from "molstar/lib/mol-data/int";
|
|
12
10
|
import { Structure, StructureElement, StructureProperties, Unit } from "molstar/lib/mol-model/structure";
|
|
11
|
+
import { clearStructureTransparency, setStructureTransparency } from "molstar/lib/mol-plugin-state/helpers/structure-transparency";
|
|
13
12
|
import { createPluginUI } from "molstar/lib/mol-plugin-ui";
|
|
14
13
|
import { renderReact18 } from "molstar/lib/mol-plugin-ui/react18";
|
|
15
14
|
import { DefaultPluginUISpec } from "molstar/lib/mol-plugin-ui/spec";
|
|
@@ -22,6 +21,7 @@ import { EveryLoci, Loci } from "molstar/lib/mol-model/loci";
|
|
|
22
21
|
import { PluginReactContext } from "molstar/lib/mol-plugin-ui/base";
|
|
23
22
|
import { ButtonsType, getButton, getButtons, getModifiers } from "molstar/lib/mol-util/input/input-observer";
|
|
24
23
|
import { throttleTime } from "rxjs/operators";
|
|
24
|
+
import { OrderedSet } from "molstar/lib/mol-data/int";
|
|
25
25
|
import { useTheme as useTheme$1 } from "@mui/material";
|
|
26
26
|
import { PluginStateObject } from "molstar/lib/mol-plugin-state/objects";
|
|
27
27
|
import { getChainOptions, getModelEntityOptions, getOperatorOptions, getSequenceWrapper, getStructureOptions } from "molstar/lib/mol-plugin-ui/sequence";
|
|
@@ -30,6 +30,7 @@ import { arrayEqual } from "molstar/lib/mol-util";
|
|
|
30
30
|
import { ParamDefinition } from "molstar/lib/mol-util/param-definition";
|
|
31
31
|
import { Toasts } from "molstar/lib/mol-plugin-ui/toast";
|
|
32
32
|
import { Viewport } from "molstar/lib/mol-plugin-ui/viewport";
|
|
33
|
+
import { Color } from "molstar/lib/mol-util/color";
|
|
33
34
|
import { PluginCommands } from "molstar/lib/mol-plugin/commands";
|
|
34
35
|
import { Vec3 } from "molstar/lib/mol-math/linear-algebra";
|
|
35
36
|
import { BondType } from "molstar/lib/mol-model/structure/model/types";
|
|
@@ -1206,249 +1207,183 @@ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChai
|
|
|
1206
1207
|
});
|
|
1207
1208
|
}
|
|
1208
1209
|
//#endregion
|
|
1209
|
-
//#region
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
function selectionKey(selection) {
|
|
1219
|
-
var _selection$residues, _selection$chains;
|
|
1220
|
-
const residues = [...new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : [])].sort((a, b) => a - b);
|
|
1221
|
-
return `${[...new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : [])].sort().join(",")}|${residues.join(",")}`;
|
|
1210
|
+
//#region \0@oxc-project+runtime@0.134.0/helpers/esm/asyncToGenerator.js
|
|
1211
|
+
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
1212
|
+
try {
|
|
1213
|
+
var i = n[a](c), u = i.value;
|
|
1214
|
+
} catch (n) {
|
|
1215
|
+
e(n);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
1222
1219
|
}
|
|
1223
|
-
|
|
1224
|
-
function
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1220
|
+
function _asyncToGenerator(n) {
|
|
1221
|
+
return function() {
|
|
1222
|
+
var t = this, e = arguments;
|
|
1223
|
+
return new Promise(function(r, o) {
|
|
1224
|
+
var a = n.apply(t, e);
|
|
1225
|
+
function _next(n) {
|
|
1226
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
1227
|
+
}
|
|
1228
|
+
function _throw(n) {
|
|
1229
|
+
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
1230
|
+
}
|
|
1231
|
+
_next(void 0);
|
|
1232
|
+
});
|
|
1233
|
+
};
|
|
1228
1234
|
}
|
|
1235
|
+
//#endregion
|
|
1236
|
+
//#region src/core/ProteinStructureViewer/hooks/useChainHighlight.ts
|
|
1229
1237
|
/**
|
|
1230
|
-
*
|
|
1238
|
+
* How transparent the other chains go while one is pointed at.
|
|
1231
1239
|
*
|
|
1232
|
-
*
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
* selected chain is counted once.
|
|
1240
|
+
* Enough that the chain under the pointer is the one being read, not enough
|
|
1241
|
+
* that the rest of the complex stops being context for it. 0 is opaque, 1 is
|
|
1242
|
+
* invisible.
|
|
1236
1243
|
*/
|
|
1237
|
-
|
|
1238
|
-
var _selection$residues3, _selection$chains3;
|
|
1239
|
-
const residues = new Set((_selection$residues3 = selection.residues) !== null && _selection$residues3 !== void 0 ? _selection$residues3 : []);
|
|
1240
|
-
for (const chainId of (_selection$chains3 = selection.chains) !== null && _selection$chains3 !== void 0 ? _selection$chains3 : []) {
|
|
1241
|
-
var _residuesByChain$get;
|
|
1242
|
-
for (const index of (_residuesByChain$get = residuesByChain.get(chainId)) !== null && _residuesByChain$get !== void 0 ? _residuesByChain$get : []) residues.add(index);
|
|
1243
|
-
}
|
|
1244
|
-
return [...residues].sort((a, b) => a - b);
|
|
1245
|
-
}
|
|
1244
|
+
const CHAIN_DIM_TRANSPARENCY = .65;
|
|
1246
1245
|
/**
|
|
1247
|
-
*
|
|
1246
|
+
* The chain id in a component key.
|
|
1248
1247
|
*
|
|
1249
|
-
*
|
|
1250
|
-
*
|
|
1251
|
-
*
|
|
1252
|
-
* named
|
|
1253
|
-
* three-letter code and the number the file gives it.
|
|
1248
|
+
* Chain components are tagged `structure-component-<part>-<chainId>`, and a
|
|
1249
|
+
* key is the component's tags sorted and joined with commas. Anchoring on the
|
|
1250
|
+
* part name and stopping at the comma is what keeps `A` from also matching the
|
|
1251
|
+
* chain named `AA`.
|
|
1254
1252
|
*/
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1253
|
+
const COMPONENT_CHAIN_ID = /structure-component-(?:polymer|ligand)-([^,]+)/;
|
|
1254
|
+
/** The chain a component was built for, or undefined if it is not a chain's. */
|
|
1255
|
+
function componentChainId(component) {
|
|
1256
|
+
var _component$key, _COMPONENT_CHAIN_ID$e;
|
|
1257
|
+
const key = (_component$key = component.key) !== null && _component$key !== void 0 ? _component$key : component.cell.transform.ref;
|
|
1258
|
+
return (_COMPONENT_CHAIN_ID$e = COMPONENT_CHAIN_ID.exec(key)) === null || _COMPONENT_CHAIN_ID$e === void 0 ? void 0 : _COMPONENT_CHAIN_ID$e[1];
|
|
1260
1259
|
}
|
|
1261
|
-
/**
|
|
1262
|
-
function
|
|
1263
|
-
|
|
1264
|
-
let count = 0;
|
|
1265
|
-
for (const index of residues) {
|
|
1266
|
-
const value = read(index);
|
|
1267
|
-
if (value === null || value === void 0) continue;
|
|
1268
|
-
sum += value;
|
|
1269
|
-
count++;
|
|
1270
|
-
}
|
|
1271
|
-
return count === 0 ? null : sum / count;
|
|
1260
|
+
/** Every chain component of every loaded structure, polymers and ligands. */
|
|
1261
|
+
function structureComponents(plugin) {
|
|
1262
|
+
return plugin.managers.structure.hierarchy.current.structures.flatMap((entry) => entry.components);
|
|
1272
1263
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
* Every atom the predicate accepts, as geometry Mol* can focus, frame and mark,
|
|
1277
|
-
* or undefined when it accepts none.
|
|
1278
|
-
*
|
|
1279
|
-
* One pass over the structure rather than a pass per residue: a whole-chain
|
|
1280
|
-
* selection names hundreds of residues, and resolving them one at a time would
|
|
1281
|
-
* walk the structure once for each.
|
|
1282
|
-
*
|
|
1283
|
-
* Atoms are bucketed by the unit they belong to because element indices are
|
|
1284
|
-
* unit-local. That bucketing is also what lets a result span a chain break: it
|
|
1285
|
-
* carries an entry per unit, which is what a range across two chains needs and
|
|
1286
|
-
* what a single unit-local entry cannot express.
|
|
1287
|
-
*
|
|
1288
|
-
* Whole residues are collected rather than one atom each, since the caller
|
|
1289
|
-
* frames the camera on the loci's bounding sphere and a lone atom would zoom
|
|
1290
|
-
* far closer than the residue warrants.
|
|
1291
|
-
*/
|
|
1292
|
-
function lociMatching(structure, matches) {
|
|
1293
|
-
const location = StructureElement.Location.create(structure);
|
|
1294
|
-
const elements = [];
|
|
1295
|
-
for (const unit of structure.units) {
|
|
1296
|
-
location.unit = unit;
|
|
1297
|
-
const indices = [];
|
|
1298
|
-
for (let i = 0; i < unit.elements.length; i++) {
|
|
1299
|
-
location.element = unit.elements[i];
|
|
1300
|
-
if (matches(location)) indices.push(i);
|
|
1301
|
-
}
|
|
1302
|
-
if (indices.length > 0) elements.push({
|
|
1303
|
-
indices,
|
|
1304
|
-
unit
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
if (elements.length === 0) return void 0;
|
|
1308
|
-
return StructureElement.Loci(structure, elements.map(({ indices, unit }) => ({
|
|
1309
|
-
indices: OrderedSet.ofSortedArray(indices),
|
|
1310
|
-
unit
|
|
1311
|
-
})));
|
|
1264
|
+
/** A set of chain ids as one comparable string. */
|
|
1265
|
+
function chainsKey(chains) {
|
|
1266
|
+
return [...chains].sort().join("\0");
|
|
1312
1267
|
}
|
|
1313
1268
|
/**
|
|
1314
|
-
*
|
|
1269
|
+
* Makes every chain outside `lit` transparent, or all of them opaque again when
|
|
1270
|
+
* nothing is lit.
|
|
1315
1271
|
*
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1318
|
-
*
|
|
1319
|
-
* part of the state tree the chain's own visibility does not reach, and a
|
|
1320
|
-
* hidden chain left in the loci would keep showing up there after its cartoon
|
|
1321
|
-
* had gone.
|
|
1322
|
-
*/
|
|
1323
|
-
function lociForSelectionInStructure(structure, selection, hiddenChains) {
|
|
1324
|
-
var _selection$residues, _selection$chains;
|
|
1325
|
-
if (isEmptySelection(selection)) return void 0;
|
|
1326
|
-
const residues = new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : []);
|
|
1327
|
-
const chains = new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : []);
|
|
1328
|
-
return lociMatching(structure, (location) => {
|
|
1329
|
-
const chainId = StructureProperties.chain.auth_asym_id(location);
|
|
1330
|
-
if (hiddenChains === null || hiddenChains === void 0 ? void 0 : hiddenChains.has(chainId)) return false;
|
|
1331
|
-
return residues.has(StructureProperties.residue.key(location)) || chains.has(chainId);
|
|
1332
|
-
});
|
|
1333
|
-
}
|
|
1334
|
-
/**
|
|
1335
|
-
* Every atom of one chain's polymer, in the first structure that has one.
|
|
1272
|
+
* Transparency layers accumulate, so what is already there is cleared before
|
|
1273
|
+
* the next chain is dimmed rather than added to - otherwise moving along the
|
|
1274
|
+
* list would leave every chain it passed dimmed behind it.
|
|
1336
1275
|
*
|
|
1337
|
-
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
* own.
|
|
1276
|
+
* False when there is no structure to write to yet, which is what a selection
|
|
1277
|
+
* arriving before the first load looks like. It is applied again after the load
|
|
1278
|
+
* rather than being taken as done.
|
|
1341
1279
|
*/
|
|
1342
|
-
function
|
|
1343
|
-
|
|
1344
|
-
var _entry$cell$obj;
|
|
1345
|
-
const structure = (_entry$cell$obj = entry.cell.obj) === null || _entry$cell$obj === void 0 ? void 0 : _entry$cell$obj.data;
|
|
1346
|
-
if (!structure) continue;
|
|
1347
|
-
const loci = lociMatching(structure, (location) => StructureProperties.chain.auth_asym_id(location) === chainId && StructureProperties.entity.type(location) === "polymer");
|
|
1348
|
-
if (loci) return loci;
|
|
1349
|
-
}
|
|
1280
|
+
function dimChainsOutside(_x2, _x3) {
|
|
1281
|
+
return _dimChainsOutside.apply(this, arguments);
|
|
1350
1282
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1283
|
+
function _dimChainsOutside() {
|
|
1284
|
+
_dimChainsOutside = _asyncToGenerator(function* (plugin, lit) {
|
|
1285
|
+
const components = structureComponents(plugin);
|
|
1286
|
+
if (components.length === 0) return false;
|
|
1287
|
+
yield clearStructureTransparency(plugin, components);
|
|
1288
|
+
if (lit.size === 0) return true;
|
|
1289
|
+
const dimmed = components.filter((component) => {
|
|
1290
|
+
const chainId = componentChainId(component);
|
|
1291
|
+
return chainId !== void 0 && !lit.has(chainId);
|
|
1292
|
+
});
|
|
1293
|
+
if (dimmed.length === 0) return true;
|
|
1294
|
+
yield setStructureTransparency(plugin, dimmed, CHAIN_DIM_TRANSPARENCY, function() {
|
|
1295
|
+
var _ref = _asyncToGenerator(function* (structure) {
|
|
1296
|
+
return Structure.toStructureElementLoci(structure);
|
|
1297
|
+
});
|
|
1298
|
+
return function(_x) {
|
|
1299
|
+
return _ref.apply(this, arguments);
|
|
1300
|
+
};
|
|
1301
|
+
}());
|
|
1302
|
+
return true;
|
|
1362
1303
|
});
|
|
1304
|
+
return _dimChainsOutside.apply(this, arguments);
|
|
1363
1305
|
}
|
|
1364
1306
|
/**
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1367
|
-
*
|
|
1368
|
-
*/
|
|
1369
|
-
function lociForSelection(plugin, selection, hiddenChains) {
|
|
1370
|
-
for (const entry of plugin.managers.structure.hierarchy.current.structures) {
|
|
1371
|
-
var _entry$cell$obj2;
|
|
1372
|
-
const structure = (_entry$cell$obj2 = entry.cell.obj) === null || _entry$cell$obj2 === void 0 ? void 0 : _entry$cell$obj2.data;
|
|
1373
|
-
if (!structure) continue;
|
|
1374
|
-
const loci = lociForSelectionInStructure(structure, selection, hiddenChains);
|
|
1375
|
-
if (loci) return loci;
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
//#endregion
|
|
1379
|
-
//#region src/core/ProteinStructureViewer/hooks/useChainHighlight.ts
|
|
1380
|
-
/**
|
|
1381
|
-
* What a chain lights up in while its name is under the pointer.
|
|
1307
|
+
* Leaves one chain lit and dims the rest: the chain whose name is pointed at,
|
|
1308
|
+
* in the sequence panel's captions or the chain legend's rows, or the chain the
|
|
1309
|
+
* selection covers once the pointer has gone.
|
|
1382
1310
|
*
|
|
1383
|
-
*
|
|
1384
|
-
*
|
|
1385
|
-
*
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
* overlay. A saturated pink is in none of those, so it cannot be mistaken for
|
|
1389
|
-
* the structure's own coloring.
|
|
1390
|
-
*/
|
|
1391
|
-
const CHAIN_HIGHLIGHT_COLOR = Color.fromRgb(200, 45, 149);
|
|
1392
|
-
/**
|
|
1393
|
-
* Stronger than the residue hover's tint, for the same reason. A whole chain
|
|
1394
|
-
* washed in 20% pink reads as a rendering artifact; at this strength it reads
|
|
1395
|
-
* as deliberate.
|
|
1396
|
-
*/
|
|
1397
|
-
const CHAIN_HIGHLIGHT_STRENGTH = .7;
|
|
1398
|
-
/**
|
|
1399
|
-
* Lights up a whole chain in the 3D view while its name is pointed at, in the
|
|
1400
|
-
* sequence panel's captions or the chain legend's rows.
|
|
1311
|
+
* The chain left lit is exactly as it was - in the chain palette, in pLDDT
|
|
1312
|
+
* bands, or in an overlay - and the others recede around it. Nothing is painted
|
|
1313
|
+
* over the top, so what the reader is being pointed at is still the color the
|
|
1314
|
+
* legend beside it is describing, and a selected chain reads the same as a
|
|
1315
|
+
* hovered one rather than sprouting a highlight of its own.
|
|
1401
1316
|
*
|
|
1402
|
-
*
|
|
1403
|
-
*
|
|
1404
|
-
* and nothing to tear down. Marking colors are a property of the renderer
|
|
1405
|
-
* rather than of the loci, though, so the pink is swapped in for the length of
|
|
1406
|
-
* the hover and the theme's own colors handed back on the way out.
|
|
1317
|
+
* A hover outranks the selection while it lasts, since pointing at a chain is a
|
|
1318
|
+
* question about that chain, and hands it back on the way out.
|
|
1407
1319
|
*
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
*
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1320
|
+
* Dimming is a state-tree commit rather than a renderer setting, so it is
|
|
1321
|
+
* applied one at a time and coalesced: a pointer crossing the list faster than
|
|
1322
|
+
* a commit finishes skips the chains it passed over and settles on the one it
|
|
1323
|
+
* came to rest on, instead of working through a queue of hovers that have
|
|
1324
|
+
* already been left.
|
|
1325
|
+
*/
|
|
1326
|
+
function useChainHighlight({ disabled, hiddenChains, loadCount, pluginRef, selectedChains }) {
|
|
1327
|
+
const hoveredRef = useRef(null);
|
|
1328
|
+
/**
|
|
1329
|
+
* The chains left lit on the canvas as it stands. A freshly loaded structure
|
|
1330
|
+
* carries no dimming at all, which is what the empty key means.
|
|
1331
|
+
*/
|
|
1332
|
+
const shownRef = useRef(chainsKey(/* @__PURE__ */ new Set()));
|
|
1333
|
+
const committingRef = useRef(false);
|
|
1334
|
+
/**
|
|
1335
|
+
* Read when a commit comes round rather than closed over, so one already in
|
|
1336
|
+
* flight settles on the current props instead of the ones it started under.
|
|
1337
|
+
*/
|
|
1338
|
+
const propsRef = useRef({
|
|
1339
|
+
disabled,
|
|
1340
|
+
hiddenChains,
|
|
1341
|
+
selectedChains
|
|
1342
|
+
});
|
|
1343
|
+
propsRef.current = {
|
|
1344
|
+
disabled,
|
|
1345
|
+
hiddenChains,
|
|
1346
|
+
selectedChains
|
|
1347
|
+
};
|
|
1348
|
+
const litChains = useCallback(() => {
|
|
1349
|
+
const { disabled: hoverOff, hiddenChains: hidden, selectedChains: selected } = propsRef.current;
|
|
1350
|
+
const hovered = hoveredRef.current;
|
|
1351
|
+
if (!hoverOff && hovered !== null && !hidden.has(hovered)) return new Set([hovered]);
|
|
1352
|
+
return new Set([...selected].filter((chainId) => !hidden.has(chainId)));
|
|
1353
|
+
}, []);
|
|
1354
|
+
const settle = useCallback(_asyncToGenerator(function* () {
|
|
1355
|
+
if (committingRef.current) return;
|
|
1356
|
+
committingRef.current = true;
|
|
1357
|
+
try {
|
|
1358
|
+
while (pluginRef.current) {
|
|
1359
|
+
const lit = litChains();
|
|
1360
|
+
const key = chainsKey(lit);
|
|
1361
|
+
if (key === shownRef.current) break;
|
|
1362
|
+
if (!(yield dimChainsOutside(pluginRef.current, lit))) break;
|
|
1363
|
+
shownRef.current = key;
|
|
1432
1364
|
}
|
|
1433
|
-
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
console.error("Failed to dim chains:", error);
|
|
1367
|
+
} finally {
|
|
1368
|
+
committingRef.current = false;
|
|
1434
1369
|
}
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1370
|
+
}), [litChains, pluginRef]);
|
|
1371
|
+
const highlightChain = useCallback((chainId) => {
|
|
1372
|
+
hoveredRef.current = chainId;
|
|
1373
|
+
settle();
|
|
1374
|
+
}, [settle]);
|
|
1375
|
+
useEffect(() => {
|
|
1376
|
+
settle();
|
|
1377
|
+
}, [
|
|
1378
|
+
disabled,
|
|
1379
|
+
hiddenChains,
|
|
1380
|
+
selectedChains,
|
|
1381
|
+
settle
|
|
1382
|
+
]);
|
|
1383
|
+
useEffect(() => {
|
|
1384
|
+
shownRef.current = chainsKey(/* @__PURE__ */ new Set());
|
|
1385
|
+
settle();
|
|
1386
|
+
}, [loadCount, settle]);
|
|
1452
1387
|
return highlightChain;
|
|
1453
1388
|
}
|
|
1454
1389
|
//#endregion
|
|
@@ -2149,13 +2084,166 @@ function useResidueMarkers({ containerRef, residueColors, sequenceWrapper }) {
|
|
|
2149
2084
|
});
|
|
2150
2085
|
}
|
|
2151
2086
|
//#endregion
|
|
2087
|
+
//#region src/core/ProteinStructureViewer/utils/selection.ts
|
|
2088
|
+
/**
|
|
2089
|
+
* A stable string for a selection, for comparing one against another.
|
|
2090
|
+
*
|
|
2091
|
+
* A consumer holding the selection in state hands back a new object on every
|
|
2092
|
+
* render, so identity says nothing about whether the selection changed. Order
|
|
2093
|
+
* is normalized too: `{ residues: [2, 1] }` and `{ residues: [1, 2] }` select
|
|
2094
|
+
* the same residues and must not read as a change.
|
|
2095
|
+
*/
|
|
2096
|
+
function selectionKey(selection) {
|
|
2097
|
+
var _selection$residues, _selection$chains;
|
|
2098
|
+
const residues = [...new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : [])].sort((a, b) => a - b);
|
|
2099
|
+
return `${[...new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : [])].sort().join(",")}|${residues.join(",")}`;
|
|
2100
|
+
}
|
|
2101
|
+
/** True when the selection names nothing at all. */
|
|
2102
|
+
function isEmptySelection(selection) {
|
|
2103
|
+
var _selection$residues2, _selection$chains2;
|
|
2104
|
+
if (!selection) return true;
|
|
2105
|
+
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);
|
|
2106
|
+
}
|
|
2107
|
+
/**
|
|
2108
|
+
* The residues a selection covers, ascending and without repeats.
|
|
2109
|
+
*
|
|
2110
|
+
* Chains are expanded through `residuesByChain`, which was read off the loaded
|
|
2111
|
+
* structure, so a chain contributes exactly the residues it holds rather than
|
|
2112
|
+
* the span between its ends. A residue named directly and also carried by a
|
|
2113
|
+
* selected chain is counted once.
|
|
2114
|
+
*/
|
|
2115
|
+
function selectionResidues(selection, residuesByChain) {
|
|
2116
|
+
var _selection$residues3, _selection$chains3;
|
|
2117
|
+
const residues = new Set((_selection$residues3 = selection.residues) !== null && _selection$residues3 !== void 0 ? _selection$residues3 : []);
|
|
2118
|
+
for (const chainId of (_selection$chains3 = selection.chains) !== null && _selection$chains3 !== void 0 ? _selection$chains3 : []) {
|
|
2119
|
+
var _residuesByChain$get;
|
|
2120
|
+
for (const index of (_residuesByChain$get = residuesByChain.get(chainId)) !== null && _residuesByChain$get !== void 0 ? _residuesByChain$get : []) residues.add(index);
|
|
2121
|
+
}
|
|
2122
|
+
return [...residues].sort((a, b) => a - b);
|
|
2123
|
+
}
|
|
2124
|
+
/**
|
|
2125
|
+
* Names a selection for the legend's readout.
|
|
2126
|
+
*
|
|
2127
|
+
* A single chain is named as one, since "Chain A" says more than the number of
|
|
2128
|
+
* residues it happens to hold. Anything else is reported as a count, which is
|
|
2129
|
+
* the only thing true of every shape a selection can take. A lone residue is
|
|
2130
|
+
* named by the caller instead, which reads it back off the structure to get its
|
|
2131
|
+
* three-letter code and the number the file gives it.
|
|
2132
|
+
*/
|
|
2133
|
+
function selectionLabel(selection, residueCount) {
|
|
2134
|
+
var _selection$chains4, _selection$residues4;
|
|
2135
|
+
const chains = (_selection$chains4 = selection.chains) !== null && _selection$chains4 !== void 0 ? _selection$chains4 : [];
|
|
2136
|
+
if (chains.length === 1 && !((_selection$residues4 = selection.residues) === null || _selection$residues4 === void 0 ? void 0 : _selection$residues4.length)) return `Chain ${chains[0]}`;
|
|
2137
|
+
return `${residueCount} residues`;
|
|
2138
|
+
}
|
|
2139
|
+
/** The mean of the values `read` returns, ignoring residues without one. */
|
|
2140
|
+
function meanOverResidues(residues, read) {
|
|
2141
|
+
let sum = 0;
|
|
2142
|
+
let count = 0;
|
|
2143
|
+
for (const index of residues) {
|
|
2144
|
+
const value = read(index);
|
|
2145
|
+
if (value === null || value === void 0) continue;
|
|
2146
|
+
sum += value;
|
|
2147
|
+
count++;
|
|
2148
|
+
}
|
|
2149
|
+
return count === 0 ? null : sum / count;
|
|
2150
|
+
}
|
|
2151
|
+
//#endregion
|
|
2152
|
+
//#region src/core/ProteinStructureViewer/utils/residueLoci.ts
|
|
2153
|
+
/**
|
|
2154
|
+
* Every atom the predicate accepts, as geometry Mol* can focus, frame and mark,
|
|
2155
|
+
* or undefined when it accepts none.
|
|
2156
|
+
*
|
|
2157
|
+
* One pass over the structure rather than a pass per residue: a whole-chain
|
|
2158
|
+
* selection names hundreds of residues, and resolving them one at a time would
|
|
2159
|
+
* walk the structure once for each.
|
|
2160
|
+
*
|
|
2161
|
+
* Atoms are bucketed by the unit they belong to because element indices are
|
|
2162
|
+
* unit-local. That bucketing is also what lets a result span a chain break: it
|
|
2163
|
+
* carries an entry per unit, which is what a range across two chains needs and
|
|
2164
|
+
* what a single unit-local entry cannot express.
|
|
2165
|
+
*
|
|
2166
|
+
* Whole residues are collected rather than one atom each, since the caller
|
|
2167
|
+
* frames the camera on the loci's bounding sphere and a lone atom would zoom
|
|
2168
|
+
* far closer than the residue warrants.
|
|
2169
|
+
*/
|
|
2170
|
+
function lociMatching(structure, matches) {
|
|
2171
|
+
const location = StructureElement.Location.create(structure);
|
|
2172
|
+
const elements = [];
|
|
2173
|
+
for (const unit of structure.units) {
|
|
2174
|
+
location.unit = unit;
|
|
2175
|
+
const indices = [];
|
|
2176
|
+
for (let i = 0; i < unit.elements.length; i++) {
|
|
2177
|
+
location.element = unit.elements[i];
|
|
2178
|
+
if (matches(location)) indices.push(i);
|
|
2179
|
+
}
|
|
2180
|
+
if (indices.length > 0) elements.push({
|
|
2181
|
+
indices,
|
|
2182
|
+
unit
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
if (elements.length === 0) return void 0;
|
|
2186
|
+
return StructureElement.Loci(structure, elements.map(({ indices, unit }) => ({
|
|
2187
|
+
indices: OrderedSet.ofSortedArray(indices),
|
|
2188
|
+
unit
|
|
2189
|
+
})));
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Every atom of the residues a selection covers, within one structure.
|
|
2193
|
+
*
|
|
2194
|
+
* `hiddenChains` is subtracted from the result. A hidden chain is not drawn, so
|
|
2195
|
+
* geometry built from it would have nothing to sit on: Mol*'s focus
|
|
2196
|
+
* representation draws its own ball-and-stick from whatever is focused, in a
|
|
2197
|
+
* part of the state tree the chain's own visibility does not reach, and a
|
|
2198
|
+
* hidden chain left in the loci would keep showing up there after its cartoon
|
|
2199
|
+
* had gone.
|
|
2200
|
+
*/
|
|
2201
|
+
function lociForSelectionInStructure(structure, selection, hiddenChains) {
|
|
2202
|
+
var _selection$residues, _selection$chains;
|
|
2203
|
+
if (isEmptySelection(selection)) return void 0;
|
|
2204
|
+
const residues = new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : []);
|
|
2205
|
+
const chains = new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : []);
|
|
2206
|
+
return lociMatching(structure, (location) => {
|
|
2207
|
+
const chainId = StructureProperties.chain.auth_asym_id(location);
|
|
2208
|
+
if (hiddenChains === null || hiddenChains === void 0 ? void 0 : hiddenChains.has(chainId)) return false;
|
|
2209
|
+
return residues.has(StructureProperties.residue.key(location)) || chains.has(chainId);
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
/**
|
|
2213
|
+
* Every atom of the residues between two 0-based indices, inclusive.
|
|
2214
|
+
*
|
|
2215
|
+
* A contiguous span is tested by comparison rather than by expanding it into a
|
|
2216
|
+
* set of indices: this runs on every residue a drag crosses, and a drag can
|
|
2217
|
+
* span the whole structure.
|
|
2218
|
+
*/
|
|
2219
|
+
function lociForResidueRange(structure, min, max) {
|
|
2220
|
+
return lociMatching(structure, (location) => {
|
|
2221
|
+
const index = StructureProperties.residue.key(location);
|
|
2222
|
+
return index >= min && index <= max;
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
/**
|
|
2226
|
+
* The same, across every structure loaded into a plugin. Results are merged
|
|
2227
|
+
* into the first structure that matched: a loci belongs to one structure, and
|
|
2228
|
+
* the residue index space already spans them as though they were one.
|
|
2229
|
+
*/
|
|
2230
|
+
function lociForSelection(plugin, selection, hiddenChains) {
|
|
2231
|
+
for (const entry of plugin.managers.structure.hierarchy.current.structures) {
|
|
2232
|
+
var _entry$cell$obj;
|
|
2233
|
+
const structure = (_entry$cell$obj = entry.cell.obj) === null || _entry$cell$obj === void 0 ? void 0 : _entry$cell$obj.data;
|
|
2234
|
+
if (!structure) continue;
|
|
2235
|
+
const loci = lociForSelectionInStructure(structure, selection, hiddenChains);
|
|
2236
|
+
if (loci) return loci;
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
//#endregion
|
|
2152
2240
|
//#region src/core/ProteinStructureViewer/utils/residueRef.ts
|
|
2153
2241
|
/**
|
|
2154
2242
|
* Reads the residue a loci points at, or null when it points at nothing the
|
|
2155
2243
|
* viewer can address.
|
|
2156
2244
|
*
|
|
2157
2245
|
* `index` comes from Mol*'s residue key, which counts residues in file order
|
|
2158
|
-
* from zero and is the same counter `
|
|
2246
|
+
* from zero and is the same counter `injectPlddt` walks. Deriving it
|
|
2159
2247
|
* from the residue's *number* instead would tie it to how the file happens to
|
|
2160
2248
|
* be numbered: a crop beginning at residue 200 would index `plddt` at 199, and
|
|
2161
2249
|
* a file numbering each chain from 1 would give two residues the same index.
|
|
@@ -2939,32 +3027,6 @@ function neutralResidueColor(mode) {
|
|
|
2939
3027
|
return Color.fromRgb(g, g, g);
|
|
2940
3028
|
}
|
|
2941
3029
|
//#endregion
|
|
2942
|
-
//#region \0@oxc-project+runtime@0.134.0/helpers/esm/asyncToGenerator.js
|
|
2943
|
-
function asyncGeneratorStep(n, t, e, r, o, a, c) {
|
|
2944
|
-
try {
|
|
2945
|
-
var i = n[a](c), u = i.value;
|
|
2946
|
-
} catch (n) {
|
|
2947
|
-
e(n);
|
|
2948
|
-
return;
|
|
2949
|
-
}
|
|
2950
|
-
i.done ? t(u) : Promise.resolve(u).then(r, o);
|
|
2951
|
-
}
|
|
2952
|
-
function _asyncToGenerator(n) {
|
|
2953
|
-
return function() {
|
|
2954
|
-
var t = this, e = arguments;
|
|
2955
|
-
return new Promise(function(r, o) {
|
|
2956
|
-
var a = n.apply(t, e);
|
|
2957
|
-
function _next(n) {
|
|
2958
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
|
|
2959
|
-
}
|
|
2960
|
-
function _throw(n) {
|
|
2961
|
-
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
|
|
2962
|
-
}
|
|
2963
|
-
_next(void 0);
|
|
2964
|
-
});
|
|
2965
|
-
};
|
|
2966
|
-
}
|
|
2967
|
-
//#endregion
|
|
2968
3030
|
//#region src/core/ProteinStructureViewer/utils/screenshot.ts
|
|
2969
3031
|
/**
|
|
2970
3032
|
* Each resolution as the Mol* preset that produces it: 1280x720, 1920x1080,
|
|
@@ -3303,6 +3365,25 @@ function focusResidue(plugin, loci) {
|
|
|
3303
3365
|
return clipRatio;
|
|
3304
3366
|
}
|
|
3305
3367
|
/**
|
|
3368
|
+
* Frames the camera on a loci without cropping anything around it.
|
|
3369
|
+
*
|
|
3370
|
+
* The counterpart to `focusResidue` for something meant to be read in context
|
|
3371
|
+
* rather than peeled down to: the camera fits what was selected, and the depth
|
|
3372
|
+
* clip is left open to the whole scene, so the rest of the structure stays on
|
|
3373
|
+
* screen. A selected chain is shown by dimming the chains around it, and they
|
|
3374
|
+
* have to be there to be dimmed.
|
|
3375
|
+
*/
|
|
3376
|
+
function frameLoci(plugin, loci) {
|
|
3377
|
+
var _plugin$canvas3d2;
|
|
3378
|
+
const camera = (_plugin$canvas3d2 = plugin.canvas3d) === null || _plugin$canvas3d2 === void 0 ? void 0 : _plugin$canvas3d2.camera;
|
|
3379
|
+
if (!camera) return;
|
|
3380
|
+
const sphere = Loci.getBoundingSphere(loci);
|
|
3381
|
+
if (!sphere) return;
|
|
3382
|
+
const framingRadius = Math.max(sphere.radius + FOCUS_EXTRA_RADIUS, FOCUS_MIN_RADIUS);
|
|
3383
|
+
const snapshot = camera.getFocus(sphere.center, framingRadius);
|
|
3384
|
+
camera.setState(_objectSpread2(_objectSpread2({}, snapshot), {}, { radius: camera.state.radiusMax }), FOCUS_DURATION_MS);
|
|
3385
|
+
}
|
|
3386
|
+
/**
|
|
3306
3387
|
* Keeps the depth clip in sync with the zoom while a residue is focused.
|
|
3307
3388
|
*
|
|
3308
3389
|
* Mol*'s trackball zoom moves the camera without touching the clip radius, so
|
|
@@ -3312,15 +3393,15 @@ function focusResidue(plugin, loci) {
|
|
|
3312
3393
|
* camera is mid-transition, so it does not fight the focus animation.
|
|
3313
3394
|
*/
|
|
3314
3395
|
function syncClipToZoom(plugin, getClipRatio) {
|
|
3315
|
-
var _plugin$
|
|
3316
|
-
const camera = (_plugin$
|
|
3396
|
+
var _plugin$canvas3d3;
|
|
3397
|
+
const camera = (_plugin$canvas3d3 = plugin.canvas3d) === null || _plugin$canvas3d3 === void 0 ? void 0 : _plugin$canvas3d3.camera;
|
|
3317
3398
|
const clipRatio = getClipRatio();
|
|
3318
3399
|
if (!camera || clipRatio === null || camera.transition.inTransition) return;
|
|
3319
3400
|
const radius = clipRadiusForDistance(Vec3.distance(camera.state.position, camera.state.target), clipRatio, camera.state.radiusMax);
|
|
3320
3401
|
if (Math.abs(camera.state.radius - radius) > CLIP_EPSILON) {
|
|
3321
|
-
var _plugin$
|
|
3402
|
+
var _plugin$canvas3d4;
|
|
3322
3403
|
camera.setState({ radius });
|
|
3323
|
-
(_plugin$
|
|
3404
|
+
(_plugin$canvas3d4 = plugin.canvas3d) === null || _plugin$canvas3d4 === void 0 || _plugin$canvas3d4.requestDraw();
|
|
3324
3405
|
}
|
|
3325
3406
|
}
|
|
3326
3407
|
//#endregion
|
|
@@ -3535,6 +3616,22 @@ function mergeMolstarSpec(base, override) {
|
|
|
3535
3616
|
return merged;
|
|
3536
3617
|
}
|
|
3537
3618
|
//#endregion
|
|
3619
|
+
//#region src/core/ProteinStructureViewer/utils/structureFormat.ts
|
|
3620
|
+
/** How much of the file to inspect when guessing the format. */
|
|
3621
|
+
const DETECT_SAMPLE = 16384;
|
|
3622
|
+
/**
|
|
3623
|
+
* Infers whether `text` is PDB or mmCIF (PDBx).
|
|
3624
|
+
*
|
|
3625
|
+
* mmCIF documents start with a `data_` block and name their coordinates
|
|
3626
|
+
* `_atom_site.`. PDB never does either, so either marker is enough. A leading
|
|
3627
|
+
* comment (`# ...`) before `data_` is still mmCIF.
|
|
3628
|
+
*/
|
|
3629
|
+
function detectStructureFormat(text) {
|
|
3630
|
+
const sample = text.slice(0, DETECT_SAMPLE);
|
|
3631
|
+
if (/(?:^|\n)\s*data_/.test(sample) || /_atom_site\./.test(sample)) return "mmcif";
|
|
3632
|
+
return "pdb";
|
|
3633
|
+
}
|
|
3634
|
+
//#endregion
|
|
3538
3635
|
//#region src/core/ProteinStructureViewer/utils/chainColorTheme.ts
|
|
3539
3636
|
/** Mol* color theme name under which per-chain coloring is registered. */
|
|
3540
3637
|
const CHAIN_COLOR_THEME_NAME = "chain-color";
|
|
@@ -3673,9 +3770,183 @@ function injectPlddtIntoPdb(pdbData, plddtValues) {
|
|
|
3673
3770
|
return paddedLine.substring(0, 60) + bfactorStr + paddedLine.substring(66);
|
|
3674
3771
|
}).join("\n");
|
|
3675
3772
|
}
|
|
3773
|
+
const ATOM_SITE_PREFIX = "_atom_site.";
|
|
3774
|
+
const B_FACTOR_COLUMN = "B_iso_or_equiv";
|
|
3775
|
+
function skipCifWhitespace(line, index) {
|
|
3776
|
+
let i = index;
|
|
3777
|
+
while (i < line.length && /\s/.test(line.charAt(i))) i += 1;
|
|
3778
|
+
return i;
|
|
3779
|
+
}
|
|
3780
|
+
function readCifToken(line, start) {
|
|
3781
|
+
const quote = line.charAt(start);
|
|
3782
|
+
if (quote === "'" || quote === "\"") {
|
|
3783
|
+
const end = line.indexOf(quote, start + 1);
|
|
3784
|
+
if (end < 0) return {
|
|
3785
|
+
next: line.length,
|
|
3786
|
+
token: line.slice(start + 1)
|
|
3787
|
+
};
|
|
3788
|
+
return {
|
|
3789
|
+
next: end + 1,
|
|
3790
|
+
token: line.slice(start + 1, end)
|
|
3791
|
+
};
|
|
3792
|
+
}
|
|
3793
|
+
let i = start;
|
|
3794
|
+
while (i < line.length && !/\s/.test(line.charAt(i))) i += 1;
|
|
3795
|
+
return {
|
|
3796
|
+
next: i,
|
|
3797
|
+
token: line.slice(start, i)
|
|
3798
|
+
};
|
|
3799
|
+
}
|
|
3800
|
+
/**
|
|
3801
|
+
* Splits one CIF line into tokens, honouring `'quoted'` and `"quoted"` values.
|
|
3802
|
+
* Atom-site rows are one line each in practice; multiline `;text;` fields are
|
|
3803
|
+
* not used there and are not handled.
|
|
3804
|
+
*/
|
|
3805
|
+
function tokenizeCifLine(line) {
|
|
3806
|
+
const tokens = [];
|
|
3807
|
+
let i = skipCifWhitespace(line, 0);
|
|
3808
|
+
while (i < line.length) {
|
|
3809
|
+
const { next, token } = readCifToken(line, i);
|
|
3810
|
+
tokens.push(token);
|
|
3811
|
+
i = skipCifWhitespace(line, next);
|
|
3812
|
+
}
|
|
3813
|
+
return tokens;
|
|
3814
|
+
}
|
|
3815
|
+
function isCifNull(value) {
|
|
3816
|
+
return value === void 0 || value === "." || value === "?";
|
|
3817
|
+
}
|
|
3818
|
+
function columnIndex(columns, name) {
|
|
3819
|
+
return columns.indexOf(name);
|
|
3820
|
+
}
|
|
3821
|
+
/**
|
|
3822
|
+
* Residue identity matching `injectPlddtIntoPdb`: chain + sequence number +
|
|
3823
|
+
* insertion code. Prefers the `auth_*` columns Mol* reports on the parsed
|
|
3824
|
+
* model, falling back to `label_*` when a file omitted them. `.` / `?`
|
|
3825
|
+
* insertion codes count as empty, the way a blank PDB column 27 does.
|
|
3826
|
+
*/
|
|
3827
|
+
function mmcifResidueKey(tokens, columns) {
|
|
3828
|
+
var _ref, _ref2, _ref3, _ref4;
|
|
3829
|
+
const authAsym = columnIndex(columns, "auth_asym_id");
|
|
3830
|
+
const labelAsym = columnIndex(columns, "label_asym_id");
|
|
3831
|
+
const authSeq = columnIndex(columns, "auth_seq_id");
|
|
3832
|
+
const labelSeq = columnIndex(columns, "label_seq_id");
|
|
3833
|
+
const ins = columnIndex(columns, "pdbx_PDB_ins_code");
|
|
3834
|
+
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]}`;
|
|
3835
|
+
}
|
|
3836
|
+
function isAtomSiteRow(tokens, columns) {
|
|
3837
|
+
const group = columnIndex(columns, "group_PDB");
|
|
3838
|
+
if (group < 0) return true;
|
|
3839
|
+
const value = tokens[group];
|
|
3840
|
+
return value === "ATOM" || value === "HETATM";
|
|
3841
|
+
}
|
|
3842
|
+
function plddtBFactor(residueIndex, plddtValues) {
|
|
3843
|
+
var _plddtValues$residueI2;
|
|
3844
|
+
return (residueIndex < plddtValues.length ? ((_plddtValues$residueI2 = plddtValues[residueIndex]) !== null && _plddtValues$residueI2 !== void 0 ? _plddtValues$residueI2 : 0) * 100 : 50).toFixed(2);
|
|
3845
|
+
}
|
|
3846
|
+
function isLoopStart(line) {
|
|
3847
|
+
return line.trim() === "loop_" || line.trim().startsWith("loop_");
|
|
3848
|
+
}
|
|
3849
|
+
function isAtomSiteColumn(line) {
|
|
3850
|
+
return line.trim().startsWith(ATOM_SITE_PREFIX);
|
|
3851
|
+
}
|
|
3852
|
+
function endsAtomSiteLoop(line) {
|
|
3853
|
+
const trimmed = line.trim();
|
|
3854
|
+
if (trimmed === "") return false;
|
|
3855
|
+
if (trimmed.startsWith("#")) return true;
|
|
3856
|
+
if (isLoopStart(trimmed)) return true;
|
|
3857
|
+
if (trimmed.startsWith("data_") || trimmed.startsWith("save_")) return true;
|
|
3858
|
+
return trimmed.startsWith("_") && !trimmed.startsWith(ATOM_SITE_PREFIX);
|
|
3859
|
+
}
|
|
3860
|
+
function isAtomSiteLoop(lines, loopIndex) {
|
|
3861
|
+
let peek = loopIndex + 1;
|
|
3862
|
+
while (peek < lines.length && lines[peek].trim() === "") peek += 1;
|
|
3863
|
+
return peek < lines.length && isAtomSiteColumn(lines[peek]);
|
|
3864
|
+
}
|
|
3865
|
+
function ensureBFactorColumn(columns, headerLines) {
|
|
3866
|
+
const existing = columnIndex(columns, B_FACTOR_COLUMN);
|
|
3867
|
+
if (existing >= 0) return existing;
|
|
3868
|
+
columns.push(B_FACTOR_COLUMN);
|
|
3869
|
+
headerLines.push(`${ATOM_SITE_PREFIX}${B_FACTOR_COLUMN}`);
|
|
3870
|
+
return columns.length - 1;
|
|
3871
|
+
}
|
|
3872
|
+
function rewriteAtomSiteRow(dataLine, columns, bIndex, plddtValues, walk) {
|
|
3873
|
+
if (dataLine.trim() === "") return dataLine;
|
|
3874
|
+
const tokens = tokenizeCifLine(dataLine);
|
|
3875
|
+
if (tokens.length === 0) return dataLine;
|
|
3876
|
+
while (tokens.length < columns.length) tokens.push(".");
|
|
3877
|
+
if (isAtomSiteRow(tokens, columns)) {
|
|
3878
|
+
const residue = mmcifResidueKey(tokens, columns);
|
|
3879
|
+
if (residue !== walk.lastResidue) {
|
|
3880
|
+
walk.residueIndex += 1;
|
|
3881
|
+
walk.lastResidue = residue;
|
|
3882
|
+
}
|
|
3883
|
+
tokens[bIndex] = plddtBFactor(walk.residueIndex, plddtValues);
|
|
3884
|
+
}
|
|
3885
|
+
return tokens.join(" ");
|
|
3886
|
+
}
|
|
3887
|
+
function rewriteAtomSiteLoop(lines, loopIndex, plddtValues, walk) {
|
|
3888
|
+
const rows = [lines[loopIndex]];
|
|
3889
|
+
let i = loopIndex + 1;
|
|
3890
|
+
const columns = [];
|
|
3891
|
+
while (i < lines.length && isAtomSiteColumn(lines[i])) {
|
|
3892
|
+
columns.push(lines[i].trim().slice(11));
|
|
3893
|
+
rows.push(lines[i]);
|
|
3894
|
+
i += 1;
|
|
3895
|
+
}
|
|
3896
|
+
const bIndex = ensureBFactorColumn(columns, rows);
|
|
3897
|
+
while (i < lines.length && !endsAtomSiteLoop(lines[i])) {
|
|
3898
|
+
rows.push(rewriteAtomSiteRow(lines[i], columns, bIndex, plddtValues, walk));
|
|
3899
|
+
i += 1;
|
|
3900
|
+
}
|
|
3901
|
+
return {
|
|
3902
|
+
next: i,
|
|
3903
|
+
rows
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
/**
|
|
3907
|
+
* Rewrites an mmCIF (PDBx) file's `_atom_site.B_iso_or_equiv` column with
|
|
3908
|
+
* pLDDT scores so Mol* can read them off the parsed model, the same way
|
|
3909
|
+
* `injectPlddtIntoPdb` rewrites the PDB B-factor column. Scores arrive on a
|
|
3910
|
+
* 0-1 scale and are stored on the conventional 0-100 one. Residues past the
|
|
3911
|
+
* end of `plddtValues` fall back to a mid-confidence 50.
|
|
3912
|
+
*
|
|
3913
|
+
* If the file has no B-factor column, one is appended to the atom-site loop
|
|
3914
|
+
* rather than leaving the scores behind.
|
|
3915
|
+
*/
|
|
3916
|
+
function injectPlddtIntoMmcif(mmcifData, plddtValues) {
|
|
3917
|
+
const lines = mmcifData.split("\n");
|
|
3918
|
+
const result = [];
|
|
3919
|
+
const walk = {
|
|
3920
|
+
lastResidue: "",
|
|
3921
|
+
residueIndex: -1
|
|
3922
|
+
};
|
|
3923
|
+
let i = 0;
|
|
3924
|
+
let rewrittenLoop = false;
|
|
3925
|
+
while (i < lines.length) {
|
|
3926
|
+
const line = lines[i];
|
|
3927
|
+
if (!rewrittenLoop && isLoopStart(line) && isAtomSiteLoop(lines, i)) {
|
|
3928
|
+
const rewritten = rewriteAtomSiteLoop(lines, i, plddtValues, walk);
|
|
3929
|
+
result.push(...rewritten.rows);
|
|
3930
|
+
i = rewritten.next;
|
|
3931
|
+
rewrittenLoop = true;
|
|
3932
|
+
continue;
|
|
3933
|
+
}
|
|
3934
|
+
result.push(line);
|
|
3935
|
+
i += 1;
|
|
3936
|
+
}
|
|
3937
|
+
return result.join("\n");
|
|
3938
|
+
}
|
|
3939
|
+
/**
|
|
3940
|
+
* Rewrites a structure file's B-factors with pLDDT scores. Dispatches to
|
|
3941
|
+
* `injectPlddtIntoPdb` or `injectPlddtIntoMmcif` from `format`, or from
|
|
3942
|
+
* detecting the format of `data` when `format` is omitted.
|
|
3943
|
+
*/
|
|
3944
|
+
function injectPlddt(data, plddtValues, format = detectStructureFormat(data)) {
|
|
3945
|
+
return format === "mmcif" ? injectPlddtIntoMmcif(data, plddtValues) : injectPlddtIntoPdb(data, plddtValues);
|
|
3946
|
+
}
|
|
3676
3947
|
/**
|
|
3677
3948
|
* Mol* color theme that reads pLDDT scores out of the B-factor column, paired
|
|
3678
|
-
* with `
|
|
3949
|
+
* with `injectPlddt`. Registered per plugin instance.
|
|
3679
3950
|
*/
|
|
3680
3951
|
const PlddtColoring = CustomElementProperty.create({
|
|
3681
3952
|
coloring: {
|
|
@@ -3985,27 +4256,30 @@ const NOTHING_LOADED = {
|
|
|
3985
4256
|
residuesByChain: /* @__PURE__ */ new Map()
|
|
3986
4257
|
};
|
|
3987
4258
|
/**
|
|
3988
|
-
* Parses the
|
|
4259
|
+
* Parses the structure and draws each chain: a cartoon over its polymer, and
|
|
3989
4260
|
* ball-and-stick over its ligands and ions.
|
|
3990
4261
|
*
|
|
3991
4262
|
* Mol*'s `default` hierarchy preset would be shorter, but it groups every
|
|
3992
4263
|
* polymer chain into a single component, and a component is the unit Mol* can
|
|
3993
4264
|
* hide. Building them per chain is what makes a chain individually hideable;
|
|
3994
4265
|
* the refs handed back are what `setSubtreeVisibility` is later pointed at.
|
|
4266
|
+
*
|
|
4267
|
+
* The wrapper detects PDB vs mmCIF from the text; this just forwards that
|
|
4268
|
+
* guess to Mol*.
|
|
3995
4269
|
*/
|
|
3996
4270
|
function loadStructure(_x9, _x10, _x11, _x12) {
|
|
3997
4271
|
return _loadStructure.apply(this, arguments);
|
|
3998
4272
|
}
|
|
3999
4273
|
function _loadStructure() {
|
|
4000
|
-
_loadStructure = _asyncToGenerator(function* (plugin,
|
|
4274
|
+
_loadStructure = _asyncToGenerator(function* (plugin, structureText, usePlddtColoring, showAxes) {
|
|
4001
4275
|
try {
|
|
4002
4276
|
var _plugin$canvas3d;
|
|
4003
4277
|
yield plugin.clear();
|
|
4004
4278
|
const data = yield plugin.builders.data.rawData({
|
|
4005
|
-
data:
|
|
4279
|
+
data: structureText,
|
|
4006
4280
|
label: "Structure"
|
|
4007
4281
|
});
|
|
4008
|
-
const trajectory = yield plugin.builders.structure.parseTrajectory(data,
|
|
4282
|
+
const trajectory = yield plugin.builders.structure.parseTrajectory(data, detectStructureFormat(structureText));
|
|
4009
4283
|
const model = yield plugin.builders.structure.createModel(trajectory);
|
|
4010
4284
|
const structure = yield plugin.builders.structure.createStructure(model, {
|
|
4011
4285
|
name: "model",
|
|
@@ -4079,16 +4353,17 @@ function _applyColorTheme() {
|
|
|
4079
4353
|
*
|
|
4080
4354
|
* The plugin is built once and then mutated in place. Creating it is expensive
|
|
4081
4355
|
* and destroys the camera, so prop changes that Mol* can absorb (background,
|
|
4082
|
-
* axes, coloring, new
|
|
4083
|
-
* sibling hooks rather than by rebuilding.
|
|
4356
|
+
* axes, coloring, new structure) are pushed in through the effects below and in
|
|
4357
|
+
* the sibling hooks rather than by rebuilding.
|
|
4084
4358
|
*/
|
|
4085
|
-
function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download, edgeColor, hasPlddt, hiddenChains, highlightColor, mode, molstarSpec, onChainHover, onChainSelect, onChainToggle, onResidueClick, onResidueHover, onSelectionChange, onSelectionClear,
|
|
4359
|
+
function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download, edgeColor, hasPlddt, hiddenChains, highlightColor, mode, molstarSpec, onChainHover, onChainSelect, onChainToggle, onResidueClick, onResidueHover, onSelectionChange, onSelectionClear, selectedChains, sequenceViewerBackgroundColor, showAxes, showSequenceViewer, structure }) {
|
|
4086
4360
|
const pluginRef = useRef(null);
|
|
4087
4361
|
const residueValueThemeRef = useRef(null);
|
|
4088
4362
|
const chainColorThemeRef = useRef(null);
|
|
4089
|
-
const
|
|
4363
|
+
const currentStructureRef = useRef(null);
|
|
4090
4364
|
const [isReady, setIsReady] = useState(false);
|
|
4091
4365
|
const [chains, setChains] = useState([]);
|
|
4366
|
+
const [loadCount, setLoadCount] = useState(0);
|
|
4092
4367
|
/**
|
|
4093
4368
|
* Where each chain's components live in the state tree, which is what the
|
|
4094
4369
|
* visibility effect points `setSubtreeVisibility` at. Rewritten by every
|
|
@@ -4145,6 +4420,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4145
4420
|
residuesByChainRef.current = loaded.residuesByChain;
|
|
4146
4421
|
applyChainVisibility(plugin, loaded.componentRefs, hiddenChainsRef.current);
|
|
4147
4422
|
setChains((prev) => chainsEqual(prev, loaded.chains) ? prev : loaded.chains);
|
|
4423
|
+
setLoadCount((count) => count + 1);
|
|
4148
4424
|
}, []);
|
|
4149
4425
|
const onResidueClickRef = useRef(onResidueClick);
|
|
4150
4426
|
onResidueClickRef.current = onResidueClick;
|
|
@@ -4163,9 +4439,9 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4163
4439
|
highlightColor,
|
|
4164
4440
|
mode,
|
|
4165
4441
|
molstarSpec,
|
|
4166
|
-
pdb,
|
|
4167
4442
|
showAxes,
|
|
4168
|
-
showSequenceViewer
|
|
4443
|
+
showSequenceViewer,
|
|
4444
|
+
structure
|
|
4169
4445
|
});
|
|
4170
4446
|
initialPropsRef.current = {
|
|
4171
4447
|
backgroundColor,
|
|
@@ -4174,9 +4450,9 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4174
4450
|
highlightColor,
|
|
4175
4451
|
mode,
|
|
4176
4452
|
molstarSpec,
|
|
4177
|
-
pdb,
|
|
4178
4453
|
showAxes,
|
|
4179
|
-
showSequenceViewer
|
|
4454
|
+
showSequenceViewer,
|
|
4455
|
+
structure
|
|
4180
4456
|
};
|
|
4181
4457
|
/**
|
|
4182
4458
|
* The consumer's `canvas3d` overrides, keyed by content. Unlike the rest of
|
|
@@ -4231,8 +4507,8 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4231
4507
|
residueValueThemeRef.current = residueValueTheme;
|
|
4232
4508
|
chainColorThemeRef.current = chainColorTheme;
|
|
4233
4509
|
const latest = initialPropsRef.current;
|
|
4234
|
-
const loaded = yield loadStructure(plugin, latest.
|
|
4235
|
-
|
|
4510
|
+
const loaded = yield loadStructure(plugin, latest.structure, latest.hasPlddt, latest.showAxes);
|
|
4511
|
+
currentStructureRef.current = latest.structure;
|
|
4236
4512
|
adoptLoadedStructure(plugin, loaded);
|
|
4237
4513
|
/**
|
|
4238
4514
|
* Mol*'s click behavior is a BehaviorSubject, so subscribing replays
|
|
@@ -4294,7 +4570,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4294
4570
|
chainColorThemeRef.current = null;
|
|
4295
4571
|
componentRefsRef.current = /* @__PURE__ */ new Map();
|
|
4296
4572
|
residuesByChainRef.current = /* @__PURE__ */ new Map();
|
|
4297
|
-
|
|
4573
|
+
currentStructureRef.current = null;
|
|
4298
4574
|
clipRatioRef.current = null;
|
|
4299
4575
|
setIsReady(false);
|
|
4300
4576
|
};
|
|
@@ -4353,16 +4629,16 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4353
4629
|
useEffect(() => {
|
|
4354
4630
|
const plugin = pluginRef.current;
|
|
4355
4631
|
if (!plugin || !isReady) return;
|
|
4356
|
-
if (
|
|
4357
|
-
|
|
4632
|
+
if (structure === currentStructureRef.current) return;
|
|
4633
|
+
currentStructureRef.current = structure;
|
|
4358
4634
|
clipRatioRef.current = null;
|
|
4359
4635
|
lastHoverRef.current = null;
|
|
4360
|
-
loadStructure(plugin,
|
|
4636
|
+
loadStructure(plugin, structure, hasPlddt, showAxes).then((loaded) => {
|
|
4361
4637
|
if (pluginRef.current !== plugin) return;
|
|
4362
4638
|
adoptLoadedStructure(plugin, loaded);
|
|
4363
4639
|
});
|
|
4364
4640
|
}, [
|
|
4365
|
-
|
|
4641
|
+
structure,
|
|
4366
4642
|
hasPlddt,
|
|
4367
4643
|
isReady,
|
|
4368
4644
|
adoptLoadedStructure
|
|
@@ -4376,6 +4652,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4376
4652
|
chainColorThemeRef,
|
|
4377
4653
|
chains,
|
|
4378
4654
|
isReady,
|
|
4655
|
+
loadCount,
|
|
4379
4656
|
pluginRef,
|
|
4380
4657
|
residuesByChainRef,
|
|
4381
4658
|
residueValueThemeRef,
|
|
@@ -4491,10 +4768,14 @@ function focusWithShell(plugin, loci, includeNeighbours, currentShell, pluginRef
|
|
|
4491
4768
|
/**
|
|
4492
4769
|
* Makes `selection` the one thing that decides where the camera sits.
|
|
4493
4770
|
*
|
|
4494
|
-
* Setting the prop frames whatever it covers
|
|
4495
|
-
*
|
|
4496
|
-
*
|
|
4497
|
-
*
|
|
4771
|
+
* Setting the prop frames whatever it covers; clearing it drops the focus and
|
|
4772
|
+
* zooms back out. The camera frames the selection's bounding sphere, so a range
|
|
4773
|
+
* or a whole chain is fitted rather than approached as a point - one residue is
|
|
4774
|
+
* just the smallest case of that.
|
|
4775
|
+
*
|
|
4776
|
+
* Residues are also marked, which is what draws Mol*'s ball-and-stick and
|
|
4777
|
+
* outline over them. Whole chains are not: those are shown by dimming the
|
|
4778
|
+
* chains around them instead, in `useChainHighlight`.
|
|
4498
4779
|
*
|
|
4499
4780
|
* The click subscription only reports the selection it was given, so a click
|
|
4500
4781
|
* moves the camera only once the consumer accepts it and echoes it back -
|
|
@@ -4503,6 +4784,7 @@ function focusWithShell(plugin, loci, includeNeighbours, currentShell, pluginRef
|
|
|
4503
4784
|
* responds to a click whether or not the consumer wires the prop up.
|
|
4504
4785
|
*/
|
|
4505
4786
|
function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setClipRatio }) {
|
|
4787
|
+
var _selection$chains, _selection$residues;
|
|
4506
4788
|
/**
|
|
4507
4789
|
* What this hook last pointed the camera at, as a key rather than the object
|
|
4508
4790
|
* itself: a consumer echoing a selection back builds a new object each time,
|
|
@@ -4527,6 +4809,11 @@ function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setCli
|
|
|
4527
4809
|
* echoing an equal selection back from refocusing on every render.
|
|
4528
4810
|
*/
|
|
4529
4811
|
const focusKey = selection ? `${selectionKey(selection)}#${[...hiddenChains].sort().join(",")}` : null;
|
|
4812
|
+
/**
|
|
4813
|
+
* Whether the selection names chains and nothing else, which is the shape
|
|
4814
|
+
* shown by dimming the chains around it rather than by focusing it.
|
|
4815
|
+
*/
|
|
4816
|
+
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));
|
|
4530
4817
|
useEffect(() => {
|
|
4531
4818
|
var _chosen$chains;
|
|
4532
4819
|
const plugin = pluginRef.current;
|
|
@@ -4547,6 +4834,12 @@ function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setCli
|
|
|
4547
4834
|
clearFocus(plugin, setClipRatio, false);
|
|
4548
4835
|
return;
|
|
4549
4836
|
}
|
|
4837
|
+
if (chainsOnly) {
|
|
4838
|
+
plugin.managers.structure.focus.clear();
|
|
4839
|
+
frameLoci(plugin, loci);
|
|
4840
|
+
setClipRatio(null);
|
|
4841
|
+
return;
|
|
4842
|
+
}
|
|
4550
4843
|
return focusWithShell(plugin, loci, !((_chosen$chains = chosen.chains) === null || _chosen$chains === void 0 ? void 0 : _chosen$chains.length), neighbourhoodRef, pluginRef, setClipRatio);
|
|
4551
4844
|
}, [
|
|
4552
4845
|
focusKey,
|
|
@@ -4708,7 +5001,7 @@ const _excluded$1 = [
|
|
|
4708
5001
|
"onResidueClick",
|
|
4709
5002
|
"onResidueHover",
|
|
4710
5003
|
"onSelectionChange",
|
|
4711
|
-
"
|
|
5004
|
+
"structure",
|
|
4712
5005
|
"plddt",
|
|
4713
5006
|
"residueOverlay",
|
|
4714
5007
|
"selection",
|
|
@@ -4776,14 +5069,14 @@ function resolveScaleProps(overlay, hasPlddt) {
|
|
|
4776
5069
|
}
|
|
4777
5070
|
var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
|
|
4778
5071
|
var _selection$chains;
|
|
4779
|
-
const { backgroundColor, chainColors: chainColorOverrides, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, onChainVisibilityChange, onChainsChange, molstarSpec, onResidueClick, onResidueHover, onSelectionChange,
|
|
5072
|
+
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);
|
|
4780
5073
|
const theme = useTheme();
|
|
4781
5074
|
const mode = getMode({ theme });
|
|
4782
5075
|
const semanticColors = getSemanticColors({ theme });
|
|
4783
5076
|
const pluginMountRef = useRef(null);
|
|
4784
5077
|
const hasPlddt = Boolean(plddt && plddt.length > 0);
|
|
4785
|
-
const
|
|
4786
|
-
|
|
5078
|
+
const processedStructure = useMemo(() => hasPlddt ? injectPlddt(structure, plddt) : structure, [
|
|
5079
|
+
structure,
|
|
4787
5080
|
plddt,
|
|
4788
5081
|
hasPlddt
|
|
4789
5082
|
]);
|
|
@@ -4830,13 +5123,14 @@ var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
|
|
|
4830
5123
|
onSelectionChange === null || onSelectionChange === void 0 || onSelectionChange(isOnlyThisChain ? null : { chains: [chainId] });
|
|
4831
5124
|
}, [onSelectionChange, selection]);
|
|
4832
5125
|
/**
|
|
4833
|
-
*
|
|
4834
|
-
* sequence panel's captions
|
|
5126
|
+
* Dims every other chain while a chain's name is pointed at, in the legend
|
|
5127
|
+
* or in the sequence panel's captions, and leaves the selected chain lit
|
|
5128
|
+
* once the pointer has gone.
|
|
4835
5129
|
*
|
|
4836
|
-
* Reached through a ref because the two ends need each other:
|
|
4837
|
-
*
|
|
4838
|
-
* has to be handed the callback to give the captions it renders. This
|
|
4839
|
-
* is stable and forwards to whatever the highlight hook installs once
|
|
5130
|
+
* Reached through a ref because the two ends need each other: dimming
|
|
5131
|
+
* other chains takes the plugin, which the hook below creates, and that
|
|
5132
|
+
* hook has to be handed the callback to give the captions it renders. This
|
|
5133
|
+
* one is stable and forwards to whatever the highlight hook installs once
|
|
4840
5134
|
* there is a plugin for it to talk to.
|
|
4841
5135
|
*/
|
|
4842
5136
|
const highlightChainRef = useRef(() => void 0);
|
|
@@ -4850,7 +5144,7 @@ var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
|
|
|
4850
5144
|
hiddenChains: hiddenChainsProp,
|
|
4851
5145
|
onChainVisibilityChange
|
|
4852
5146
|
});
|
|
4853
|
-
const { chainColorThemeRef, chains: loadedChains, isReady, pluginRef, residuesByChainRef, residueValueThemeRef, setClipRatio } = useMolstarPlugin({
|
|
5147
|
+
const { chainColorThemeRef, chains: loadedChains, isReady, loadCount, pluginRef, residuesByChainRef, residueValueThemeRef, setClipRatio } = useMolstarPlugin({
|
|
4854
5148
|
backgroundColor: bgColor,
|
|
4855
5149
|
chainColors,
|
|
4856
5150
|
containerRef: pluginMountRef,
|
|
@@ -4868,15 +5162,18 @@ var ProteinStructureViewer_default = memo(forwardRef((props, ref) => {
|
|
|
4868
5162
|
onResidueHover: handleResidueHover,
|
|
4869
5163
|
onSelectionChange,
|
|
4870
5164
|
onSelectionClear: handleSelectionClear,
|
|
4871
|
-
pdb: processedPdb,
|
|
4872
5165
|
selectedChains,
|
|
4873
5166
|
sequenceViewerBackgroundColor,
|
|
4874
5167
|
showAxes,
|
|
4875
|
-
showSequenceViewer
|
|
5168
|
+
showSequenceViewer,
|
|
5169
|
+
structure: processedStructure
|
|
4876
5170
|
});
|
|
4877
5171
|
highlightChainRef.current = useChainHighlight({
|
|
4878
5172
|
disabled: disableChainHighlightOnHover,
|
|
4879
|
-
|
|
5173
|
+
hiddenChains,
|
|
5174
|
+
loadCount,
|
|
5175
|
+
pluginRef,
|
|
5176
|
+
selectedChains
|
|
4880
5177
|
});
|
|
4881
5178
|
useEffect(() => {
|
|
4882
5179
|
setChains(loadedChains);
|
|
@@ -5421,4 +5718,4 @@ const StackedBarChart = (props) => {
|
|
|
5421
5718
|
return /* @__PURE__ */ jsxs(ChartWrapper, _objectSpread2(_objectSpread2({ width }, rest), {}, { children: [/* @__PURE__ */ jsxs(TitleContainer, { children: [/* @__PURE__ */ jsx(ChartTitle, { children: title }), !hideBadge && displayBadge && /* @__PURE__ */ jsx(StyledBadge, { children: displayBadge })] }), chartContent] }));
|
|
5422
5719
|
};
|
|
5423
5720
|
//#endregion
|
|
5424
|
-
export { DOWNLOAD_RESOLUTIONS, HeatmapChart_default as HeatmapChart, PLASMA_COLOR_SCALE, PLDDT_COLOR_SCALE, ProteinStructureViewer_default as ProteinStructureViewer, StackedBarChart, injectPlddtIntoPdb, sampleColorScale, toCssGradient };
|
|
5721
|
+
export { DOWNLOAD_RESOLUTIONS, HeatmapChart_default as HeatmapChart, PLASMA_COLOR_SCALE, PLDDT_COLOR_SCALE, ProteinStructureViewer_default as ProteinStructureViewer, StackedBarChart, detectStructureFormat, injectPlddt, injectPlddtIntoMmcif, injectPlddtIntoPdb, sampleColorScale, toCssGradient };
|