@czi-sds/data-viz 2.4.2 → 2.5.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/ProteinStructureScene.cjs.d.ts +2 -0
- package/dist/ProteinStructureScene.cjs.js +5 -0
- package/dist/ProteinStructureScene.esm.d.ts +2 -0
- package/dist/ProteinStructureScene.esm.js +3 -0
- package/dist/ProteinStructureViewer.cjs.d.ts +3 -2
- package/dist/ProteinStructureViewer.cjs.js +9 -5
- package/dist/ProteinStructureViewer.esm.d.ts +3 -2
- package/dist/ProteinStructureViewer.esm.js +3 -2
- package/dist/StackedBarChart.cjs.d.ts +1 -1
- package/dist/StackedBarChart.esm.d.ts +1 -1
- package/dist/index.cjs.d.ts +4 -3
- package/dist/index.cjs.js +9 -5
- package/dist/index.esm.d.ts +4 -3
- package/dist/index.esm.js +3 -2
- package/dist/shared/HeatmapChart.cjs.js +2 -1
- package/dist/shared/ProteinStructureViewer.cjs.js +487 -1364
- package/dist/shared/ProteinStructureViewer.esm.js +456 -1306
- package/dist/shared/StackedBarChart.cjs.js +2 -1
- package/dist/shared/chunk.cjs.js +29 -0
- package/dist/shared/index2.cjs.d.ts +322 -66
- package/dist/shared/index2.esm.d.ts +321 -67
- package/dist/shared/index3.cjs.d.ts +66 -163
- package/dist/shared/index3.esm.d.ts +67 -162
- package/dist/shared/index4.cjs.d.ts +166 -0
- package/dist/shared/index4.esm.d.ts +166 -0
- package/dist/shared/objectSpread2.cjs.js +0 -28
- package/dist/shared/renderStructureImage.cjs.js +2360 -0
- package/dist/shared/renderStructureImage.esm.js +2157 -0
- package/package.json +12 -2
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
const require_chunk = require("./chunk.cjs.js");
|
|
2
3
|
const require_objectSpread2 = require("./objectSpread2.cjs.js");
|
|
4
|
+
const require_renderStructureImage = require("./renderStructureImage.cjs.js");
|
|
3
5
|
const require_colorScales = require("./colorScales.cjs.js");
|
|
4
6
|
let react = require("react");
|
|
5
7
|
let _emotion_styled = require("@emotion/styled");
|
|
6
|
-
_emotion_styled =
|
|
8
|
+
_emotion_styled = require_chunk.__toESM(_emotion_styled);
|
|
7
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
let molstar_lib_mol_math_linear_algebra = require("molstar/lib/mol-math/linear-algebra");
|
|
11
|
+
let molstar_lib_mol_model_loci = require("molstar/lib/mol-model/loci");
|
|
12
|
+
let molstar_lib_mol_data_int = require("molstar/lib/mol-data/int");
|
|
13
|
+
let molstar_lib_mol_model_structure = require("molstar/lib/mol-model/structure");
|
|
8
14
|
let _czi_sds_components = require("@czi-sds/components");
|
|
9
15
|
let _mui_material_styles = require("@mui/material/styles");
|
|
10
16
|
let _phosphor_icons_react = require("@phosphor-icons/react");
|
|
11
|
-
let molstar_lib_mol_model_structure = require("molstar/lib/mol-model/structure");
|
|
12
17
|
let molstar_lib_mol_plugin_state_helpers_structure_transparency = require("molstar/lib/mol-plugin-state/helpers/structure-transparency");
|
|
13
18
|
let molstar_lib_mol_plugin_ui = require("molstar/lib/mol-plugin-ui");
|
|
14
19
|
let molstar_lib_mol_plugin_ui_react18 = require("molstar/lib/mol-plugin-ui/react18");
|
|
15
20
|
let molstar_lib_mol_plugin_ui_spec = require("molstar/lib/mol-plugin-ui/spec");
|
|
16
21
|
let molstar_lib_mol_plugin_behavior = require("molstar/lib/mol-plugin/behavior");
|
|
17
|
-
let molstar_lib_mol_plugin_behavior_static_state = require("molstar/lib/mol-plugin/behavior/static/state");
|
|
18
22
|
let molstar_lib_mol_plugin_config = require("molstar/lib/mol-plugin/config");
|
|
19
23
|
let molstar_lib_mol_repr_representation = require("molstar/lib/mol-repr/representation");
|
|
20
24
|
let rxjs = require("rxjs");
|
|
21
|
-
let molstar_lib_mol_model_loci = require("molstar/lib/mol-model/loci");
|
|
22
25
|
let molstar_lib_mol_plugin_ui_base = require("molstar/lib/mol-plugin-ui/base");
|
|
23
26
|
let molstar_lib_mol_util_input_input_observer = require("molstar/lib/mol-util/input/input-observer");
|
|
24
27
|
let rxjs_operators = require("rxjs/operators");
|
|
25
|
-
let molstar_lib_mol_data_int = require("molstar/lib/mol-data/int");
|
|
26
28
|
let _mui_material = require("@mui/material");
|
|
27
29
|
let molstar_lib_mol_plugin_state_objects = require("molstar/lib/mol-plugin-state/objects");
|
|
28
30
|
let molstar_lib_mol_plugin_ui_sequence = require("molstar/lib/mol-plugin-ui/sequence");
|
|
@@ -31,13 +33,7 @@ let molstar_lib_mol_util = require("molstar/lib/mol-util");
|
|
|
31
33
|
let molstar_lib_mol_util_param_definition = require("molstar/lib/mol-util/param-definition");
|
|
32
34
|
let molstar_lib_mol_plugin_ui_toast = require("molstar/lib/mol-plugin-ui/toast");
|
|
33
35
|
let molstar_lib_mol_plugin_ui_viewport = require("molstar/lib/mol-plugin-ui/viewport");
|
|
34
|
-
let molstar_lib_mol_util_color = require("molstar/lib/mol-util/color");
|
|
35
36
|
let molstar_lib_mol_plugin_commands = require("molstar/lib/mol-plugin/commands");
|
|
36
|
-
let molstar_lib_mol_math_linear_algebra = require("molstar/lib/mol-math/linear-algebra");
|
|
37
|
-
let molstar_lib_mol_model_structure_model_types = require("molstar/lib/mol-model/structure/model/types");
|
|
38
|
-
let molstar_lib_mol_theme_label = require("molstar/lib/mol-theme/label");
|
|
39
|
-
let molstar_lib_mol_script_language_builder = require("molstar/lib/mol-script/language/builder");
|
|
40
|
-
let molstar_lib_mol_model_props_common_custom_element_property = require("molstar/lib/mol-model-props/common/custom-element-property");
|
|
41
37
|
let molstar_lib_mol_plugin_behavior_dynamic_selection_structure_focus_representation = require("molstar/lib/mol-plugin/behavior/dynamic/selection/structure-focus-representation");
|
|
42
38
|
//#region src/core/ProteinStructureViewer/components/ChainLegend/style.ts
|
|
43
39
|
/** Opacity applied to a hidden chain's row, so it reads as off but reachable. */
|
|
@@ -690,30 +686,71 @@ function StructureLegend({ chainBandColors, chainColors, chains = [], hiddenChai
|
|
|
690
686
|
});
|
|
691
687
|
}
|
|
692
688
|
//#endregion
|
|
693
|
-
//#region
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
689
|
+
//#region src/core/ProteinStructureViewer/hooks/useCamera.ts
|
|
690
|
+
/**
|
|
691
|
+
* How long the camera has to stay put before it counts as having come to
|
|
692
|
+
* rest, in ms. Mol* draws on demand, so a camera that has stopped stops the
|
|
693
|
+
* draws, and a trackball still coasting after a drag keeps pushing this back.
|
|
694
|
+
*/
|
|
695
|
+
const CAMERA_SETTLE_MS = 150;
|
|
696
|
+
/**
|
|
697
|
+
* Keeps the camera in step with the camera props after a structure is up:
|
|
698
|
+
* the projection, the orientation, and reporting where the camera comes to
|
|
699
|
+
* rest. Where the camera starts on a structure is decided by the load, which
|
|
700
|
+
* knows when there is something drawn to place it against.
|
|
701
|
+
*/
|
|
702
|
+
function useCamera({ framedOrientationRef, highlights, isReady, onCameraChange, orientation, pluginRef, projection, sceneRef }) {
|
|
703
|
+
const highlightsRef = (0, react.useRef)(highlights);
|
|
704
|
+
highlightsRef.current = highlights;
|
|
705
|
+
const onCameraChangeRef = (0, react.useRef)(onCameraChange);
|
|
706
|
+
onCameraChangeRef.current = onCameraChange;
|
|
707
|
+
(0, react.useEffect)(() => {
|
|
708
|
+
var _pluginRef$current;
|
|
709
|
+
const canvas3d = (_pluginRef$current = pluginRef.current) === null || _pluginRef$current === void 0 ? void 0 : _pluginRef$current.canvas3d;
|
|
710
|
+
if (!canvas3d || !isReady || !projection) return;
|
|
711
|
+
if (canvas3d.props.camera.mode === projection) return;
|
|
712
|
+
canvas3d.setProps({ camera: { mode: projection } });
|
|
713
|
+
}, [
|
|
714
|
+
isReady,
|
|
715
|
+
pluginRef,
|
|
716
|
+
projection
|
|
717
|
+
]);
|
|
718
|
+
(0, react.useEffect)(() => {
|
|
719
|
+
const scene = sceneRef.current;
|
|
720
|
+
if (!scene || !isReady) return;
|
|
721
|
+
if (orientation === framedOrientationRef.current) return;
|
|
722
|
+
framedOrientationRef.current = orientation;
|
|
723
|
+
if (!orientation) return;
|
|
724
|
+
scene.orient(orientation, highlightsRef.current, 250);
|
|
725
|
+
}, [
|
|
726
|
+
framedOrientationRef,
|
|
727
|
+
isReady,
|
|
728
|
+
orientation,
|
|
729
|
+
sceneRef
|
|
730
|
+
]);
|
|
731
|
+
(0, react.useEffect)(() => {
|
|
732
|
+
var _pluginRef$current2;
|
|
733
|
+
const canvas3d = (_pluginRef$current2 = pluginRef.current) === null || _pluginRef$current2 === void 0 ? void 0 : _pluginRef$current2.canvas3d;
|
|
734
|
+
if (!canvas3d || !isReady) return;
|
|
735
|
+
let timer;
|
|
736
|
+
let reported = null;
|
|
737
|
+
const subscription = canvas3d.didDraw.subscribe(() => {
|
|
738
|
+
if (!onCameraChangeRef.current) return;
|
|
739
|
+
clearTimeout(timer);
|
|
740
|
+
timer = setTimeout(() => {
|
|
741
|
+
var _onCameraChangeRef$cu;
|
|
742
|
+
if (canvas3d.camera.transition.inTransition) return;
|
|
743
|
+
const state = require_renderStructureImage.readCameraState(canvas3d);
|
|
744
|
+
if (reported && require_renderStructureImage.camerasEqual(reported, state)) return;
|
|
745
|
+
reported = state;
|
|
746
|
+
(_onCameraChangeRef$cu = onCameraChangeRef.current) === null || _onCameraChangeRef$cu === void 0 || _onCameraChangeRef$cu.call(onCameraChangeRef, state);
|
|
747
|
+
}, CAMERA_SETTLE_MS);
|
|
715
748
|
});
|
|
716
|
-
|
|
749
|
+
return () => {
|
|
750
|
+
subscription.unsubscribe();
|
|
751
|
+
clearTimeout(timer);
|
|
752
|
+
};
|
|
753
|
+
}, [isReady, pluginRef]);
|
|
717
754
|
}
|
|
718
755
|
//#endregion
|
|
719
756
|
//#region src/core/ProteinStructureViewer/hooks/useChainHighlight.ts
|
|
@@ -733,13 +770,20 @@ const CHAIN_DIM_TRANSPARENCY = .65;
|
|
|
733
770
|
* part name and stopping at the comma is what keeps `A` from also matching the
|
|
734
771
|
* chain named `AA`.
|
|
735
772
|
*/
|
|
736
|
-
const COMPONENT_CHAIN_ID = /structure-component-(?:polymer|ligand)-([^,]+)/;
|
|
773
|
+
const COMPONENT_CHAIN_ID = /structure-component-(?:polymer|ligand|highlight)-([^,]+)/;
|
|
774
|
+
/** The one component spanning chains: the surface over the visible ones. */
|
|
775
|
+
const SURFACE_COMPONENT = /structure-component-surface(?:,|$)/;
|
|
737
776
|
/** The chain a component was built for, or undefined if it is not a chain's. */
|
|
738
777
|
function componentChainId(component) {
|
|
739
778
|
var _component$key, _COMPONENT_CHAIN_ID$e;
|
|
740
779
|
const key = (_component$key = component.key) !== null && _component$key !== void 0 ? _component$key : component.cell.transform.ref;
|
|
741
780
|
return (_COMPONENT_CHAIN_ID$e = COMPONENT_CHAIN_ID.exec(key)) === null || _COMPONENT_CHAIN_ID$e === void 0 ? void 0 : _COMPONENT_CHAIN_ID$e[1];
|
|
742
781
|
}
|
|
782
|
+
/** True for the surface, which draws every visible chain in one component. */
|
|
783
|
+
function isSurfaceComponent(component) {
|
|
784
|
+
var _component$key2;
|
|
785
|
+
return SURFACE_COMPONENT.test((_component$key2 = component.key) !== null && _component$key2 !== void 0 ? _component$key2 : component.cell.transform.ref);
|
|
786
|
+
}
|
|
743
787
|
/** Every chain component of every loaded structure, polymers and ligands. */
|
|
744
788
|
function structureComponents(plugin) {
|
|
745
789
|
return plugin.managers.structure.hierarchy.current.structures.flatMap((entry) => entry.components);
|
|
@@ -760,11 +804,11 @@ function chainsKey(chains) {
|
|
|
760
804
|
* arriving before the first load looks like. It is applied again after the load
|
|
761
805
|
* rather than being taken as done.
|
|
762
806
|
*/
|
|
763
|
-
function dimChainsOutside(
|
|
807
|
+
function dimChainsOutside(_x3, _x4) {
|
|
764
808
|
return _dimChainsOutside.apply(this, arguments);
|
|
765
809
|
}
|
|
766
810
|
function _dimChainsOutside() {
|
|
767
|
-
_dimChainsOutside = _asyncToGenerator(function* (plugin, lit) {
|
|
811
|
+
_dimChainsOutside = require_renderStructureImage._asyncToGenerator(function* (plugin, lit) {
|
|
768
812
|
const components = structureComponents(plugin);
|
|
769
813
|
if (components.length === 0) return false;
|
|
770
814
|
yield (0, molstar_lib_mol_plugin_state_helpers_structure_transparency.clearStructureTransparency)(plugin, components);
|
|
@@ -773,15 +817,24 @@ function _dimChainsOutside() {
|
|
|
773
817
|
const chainId = componentChainId(component);
|
|
774
818
|
return chainId !== void 0 && !lit.has(chainId);
|
|
775
819
|
});
|
|
776
|
-
if (dimmed.length
|
|
777
|
-
|
|
778
|
-
var _ref = _asyncToGenerator(function* (structure) {
|
|
820
|
+
if (dimmed.length > 0) yield (0, molstar_lib_mol_plugin_state_helpers_structure_transparency.setStructureTransparency)(plugin, dimmed, CHAIN_DIM_TRANSPARENCY, function() {
|
|
821
|
+
var _ref = require_renderStructureImage._asyncToGenerator(function* (structure) {
|
|
779
822
|
return molstar_lib_mol_model_structure.Structure.toStructureElementLoci(structure);
|
|
780
823
|
});
|
|
781
824
|
return function(_x) {
|
|
782
825
|
return _ref.apply(this, arguments);
|
|
783
826
|
};
|
|
784
827
|
}());
|
|
828
|
+
const surfaces = components.filter(isSurfaceComponent);
|
|
829
|
+
if (surfaces.length > 0) yield (0, molstar_lib_mol_plugin_state_helpers_structure_transparency.setStructureTransparency)(plugin, surfaces, CHAIN_DIM_TRANSPARENCY, function() {
|
|
830
|
+
var _ref2 = require_renderStructureImage._asyncToGenerator(function* (structure) {
|
|
831
|
+
var _lociOutsideChains;
|
|
832
|
+
return (_lociOutsideChains = require_renderStructureImage.lociOutsideChains(structure, lit)) !== null && _lociOutsideChains !== void 0 ? _lociOutsideChains : molstar_lib_mol_model_loci.EmptyLoci;
|
|
833
|
+
});
|
|
834
|
+
return function(_x2) {
|
|
835
|
+
return _ref2.apply(this, arguments);
|
|
836
|
+
};
|
|
837
|
+
}());
|
|
785
838
|
return true;
|
|
786
839
|
});
|
|
787
840
|
return _dimChainsOutside.apply(this, arguments);
|
|
@@ -806,7 +859,7 @@ function _dimChainsOutside() {
|
|
|
806
859
|
* came to rest on, instead of working through a queue of hovers that have
|
|
807
860
|
* already been left.
|
|
808
861
|
*/
|
|
809
|
-
function useChainHighlight({ disabled, hiddenChains, loadCount, pluginRef, selectedChains }) {
|
|
862
|
+
function useChainHighlight({ disabled, hiddenChains, loadCount, pluginRef, sceneVersion, selectedChains }) {
|
|
810
863
|
const hoveredRef = (0, react.useRef)(null);
|
|
811
864
|
/**
|
|
812
865
|
* The chains left lit on the canvas as it stands. A freshly loaded structure
|
|
@@ -834,7 +887,7 @@ function useChainHighlight({ disabled, hiddenChains, loadCount, pluginRef, selec
|
|
|
834
887
|
if (!hoverOff && hovered !== null && !hidden.has(hovered)) return new Set([hovered]);
|
|
835
888
|
return new Set([...selected].filter((chainId) => !hidden.has(chainId)));
|
|
836
889
|
}, []);
|
|
837
|
-
const settle = (0, react.useCallback)(_asyncToGenerator(function* () {
|
|
890
|
+
const settle = (0, react.useCallback)(require_renderStructureImage._asyncToGenerator(function* () {
|
|
838
891
|
if (committingRef.current) return;
|
|
839
892
|
committingRef.current = true;
|
|
840
893
|
try {
|
|
@@ -866,51 +919,14 @@ function useChainHighlight({ disabled, hiddenChains, loadCount, pluginRef, selec
|
|
|
866
919
|
(0, react.useEffect)(() => {
|
|
867
920
|
shownRef.current = chainsKey(/* @__PURE__ */ new Set());
|
|
868
921
|
settle();
|
|
869
|
-
}, [
|
|
922
|
+
}, [
|
|
923
|
+
loadCount,
|
|
924
|
+
sceneVersion,
|
|
925
|
+
settle
|
|
926
|
+
]);
|
|
870
927
|
return highlightChain;
|
|
871
928
|
}
|
|
872
929
|
//#endregion
|
|
873
|
-
//#region src/common/chainColors.ts
|
|
874
|
-
/**
|
|
875
|
-
* Default colors for telling chains apart, in assignment order.
|
|
876
|
-
*
|
|
877
|
-
* The Okabe-Ito qualitative palette, which is built to stay distinguishable
|
|
878
|
-
* under every common form of color blindness - the reason to prefer it over a
|
|
879
|
-
* hand-picked set, since a complex is read by which chain is which and a
|
|
880
|
-
* viewer that collapses two of them into one color stops answering that.
|
|
881
|
-
*
|
|
882
|
-
* Its black is left out: the canvas inverts with the theme, so a chain painted
|
|
883
|
-
* black would vanish against the dark background. The rest carry enough
|
|
884
|
-
* lightness to hold up against both.
|
|
885
|
-
*
|
|
886
|
-
* Chains past the end of the palette wrap around to the start.
|
|
887
|
-
*/
|
|
888
|
-
const CHAIN_COLOR_PALETTE = [
|
|
889
|
-
"#009E73",
|
|
890
|
-
"#0072B2",
|
|
891
|
-
"#E69F00",
|
|
892
|
-
"#CC79A7",
|
|
893
|
-
"#56B4E9",
|
|
894
|
-
"#D55E00",
|
|
895
|
-
"#F0E442"
|
|
896
|
-
];
|
|
897
|
-
/**
|
|
898
|
-
* Assigns each chain a color, in the order given, with `overrides` taking
|
|
899
|
-
* precedence by `chainId`.
|
|
900
|
-
*
|
|
901
|
-
* Palette positions are spent on every chain whether or not it is overridden,
|
|
902
|
-
* so overriding one chain does not shift the colors of the others.
|
|
903
|
-
*/
|
|
904
|
-
function chainColorMap(chainIds, overrides) {
|
|
905
|
-
const colors = /* @__PURE__ */ new Map();
|
|
906
|
-
chainIds.forEach((chainId, i) => {
|
|
907
|
-
var _overrides$chainId;
|
|
908
|
-
const fallback = CHAIN_COLOR_PALETTE[i % CHAIN_COLOR_PALETTE.length];
|
|
909
|
-
colors.set(chainId, (_overrides$chainId = overrides === null || overrides === void 0 ? void 0 : overrides[chainId]) !== null && _overrides$chainId !== void 0 ? _overrides$chainId : fallback);
|
|
910
|
-
});
|
|
911
|
-
return colors;
|
|
912
|
-
}
|
|
913
|
-
//#endregion
|
|
914
930
|
//#region src/core/ProteinStructureViewer/hooks/useChains.ts
|
|
915
931
|
/**
|
|
916
932
|
* Reconciles the chains found in the structure with the visibility and coloring
|
|
@@ -944,7 +960,7 @@ function useChains({ chainColors, chains, hiddenChains, onChainVisibilityChange
|
|
|
944
960
|
const hiddenKey = [...isControlled ? hiddenChains : ownHidden].sort().join("\0");
|
|
945
961
|
const hidden = (0, react.useMemo)(() => new Set(hiddenKey === "" ? [] : hiddenKey.split("\0")), [hiddenKey]);
|
|
946
962
|
return {
|
|
947
|
-
colors: (0, react.useMemo)(() => chainColorMap(chainIds, chainColors), [chainIds.map((id) => {
|
|
963
|
+
colors: (0, react.useMemo)(() => require_renderStructureImage.chainColorMap(chainIds, chainColors), [chainIds.map((id) => {
|
|
948
964
|
var _chainColors$id;
|
|
949
965
|
return `${id}:${(_chainColors$id = chainColors === null || chainColors === void 0 ? void 0 : chainColors[id]) !== null && _chainColors$id !== void 0 ? _chainColors$id : ""}`;
|
|
950
966
|
}).join("\0")]),
|
|
@@ -1578,159 +1594,6 @@ function useResidueMarkers({ containerRef, residueColors, sequenceWrapper }) {
|
|
|
1578
1594
|
});
|
|
1579
1595
|
}
|
|
1580
1596
|
//#endregion
|
|
1581
|
-
//#region src/core/ProteinStructureViewer/utils/selection.ts
|
|
1582
|
-
/**
|
|
1583
|
-
* A stable string for a selection, for comparing one against another.
|
|
1584
|
-
*
|
|
1585
|
-
* A consumer holding the selection in state hands back a new object on every
|
|
1586
|
-
* render, so identity says nothing about whether the selection changed. Order
|
|
1587
|
-
* is normalized too: `{ residues: [2, 1] }` and `{ residues: [1, 2] }` select
|
|
1588
|
-
* the same residues and must not read as a change.
|
|
1589
|
-
*/
|
|
1590
|
-
function selectionKey(selection) {
|
|
1591
|
-
var _selection$residues, _selection$chains;
|
|
1592
|
-
const residues = [...new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : [])].sort((a, b) => a - b);
|
|
1593
|
-
return `${[...new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : [])].sort().join(",")}|${residues.join(",")}`;
|
|
1594
|
-
}
|
|
1595
|
-
/** True when the selection names nothing at all. */
|
|
1596
|
-
function isEmptySelection(selection) {
|
|
1597
|
-
var _selection$residues2, _selection$chains2;
|
|
1598
|
-
if (!selection) return true;
|
|
1599
|
-
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);
|
|
1600
|
-
}
|
|
1601
|
-
/**
|
|
1602
|
-
* The residues a selection covers, ascending and without repeats.
|
|
1603
|
-
*
|
|
1604
|
-
* Chains are expanded through `residuesByChain`, which was read off the loaded
|
|
1605
|
-
* structure, so a chain contributes exactly the residues it holds rather than
|
|
1606
|
-
* the span between its ends. A residue named directly and also carried by a
|
|
1607
|
-
* selected chain is counted once.
|
|
1608
|
-
*/
|
|
1609
|
-
function selectionResidues(selection, residuesByChain) {
|
|
1610
|
-
var _selection$residues3, _selection$chains3;
|
|
1611
|
-
const residues = new Set((_selection$residues3 = selection.residues) !== null && _selection$residues3 !== void 0 ? _selection$residues3 : []);
|
|
1612
|
-
for (const chainId of (_selection$chains3 = selection.chains) !== null && _selection$chains3 !== void 0 ? _selection$chains3 : []) {
|
|
1613
|
-
var _residuesByChain$get;
|
|
1614
|
-
for (const index of (_residuesByChain$get = residuesByChain.get(chainId)) !== null && _residuesByChain$get !== void 0 ? _residuesByChain$get : []) residues.add(index);
|
|
1615
|
-
}
|
|
1616
|
-
return [...residues].sort((a, b) => a - b);
|
|
1617
|
-
}
|
|
1618
|
-
/**
|
|
1619
|
-
* Names a selection for the legend's readout.
|
|
1620
|
-
*
|
|
1621
|
-
* A single chain is named as one, since "Chain A" says more than the number of
|
|
1622
|
-
* residues it happens to hold. Anything else is reported as a count, which is
|
|
1623
|
-
* the only thing true of every shape a selection can take. A lone residue is
|
|
1624
|
-
* named by the caller instead, which reads it back off the structure to get its
|
|
1625
|
-
* three-letter code and the number the file gives it.
|
|
1626
|
-
*/
|
|
1627
|
-
function selectionLabel(selection, residueCount) {
|
|
1628
|
-
var _selection$chains4, _selection$residues4;
|
|
1629
|
-
const chains = (_selection$chains4 = selection.chains) !== null && _selection$chains4 !== void 0 ? _selection$chains4 : [];
|
|
1630
|
-
if (chains.length === 1 && !((_selection$residues4 = selection.residues) === null || _selection$residues4 === void 0 ? void 0 : _selection$residues4.length)) return `Chain ${chains[0]}`;
|
|
1631
|
-
return `${residueCount} residues`;
|
|
1632
|
-
}
|
|
1633
|
-
/** The mean of the values `read` returns, ignoring residues without one. */
|
|
1634
|
-
function meanOverResidues(residues, read) {
|
|
1635
|
-
let sum = 0;
|
|
1636
|
-
let count = 0;
|
|
1637
|
-
for (const index of residues) {
|
|
1638
|
-
const value = read(index);
|
|
1639
|
-
if (value === null || value === void 0) continue;
|
|
1640
|
-
sum += value;
|
|
1641
|
-
count++;
|
|
1642
|
-
}
|
|
1643
|
-
return count === 0 ? null : sum / count;
|
|
1644
|
-
}
|
|
1645
|
-
//#endregion
|
|
1646
|
-
//#region src/core/ProteinStructureViewer/utils/residueLoci.ts
|
|
1647
|
-
/**
|
|
1648
|
-
* Every atom the predicate accepts, as geometry Mol* can focus, frame and mark,
|
|
1649
|
-
* or undefined when it accepts none.
|
|
1650
|
-
*
|
|
1651
|
-
* One pass over the structure rather than a pass per residue: a whole-chain
|
|
1652
|
-
* selection names hundreds of residues, and resolving them one at a time would
|
|
1653
|
-
* walk the structure once for each.
|
|
1654
|
-
*
|
|
1655
|
-
* Atoms are bucketed by the unit they belong to because element indices are
|
|
1656
|
-
* unit-local. That bucketing is also what lets a result span a chain break: it
|
|
1657
|
-
* carries an entry per unit, which is what a range across two chains needs and
|
|
1658
|
-
* what a single unit-local entry cannot express.
|
|
1659
|
-
*
|
|
1660
|
-
* Whole residues are collected rather than one atom each, since the caller
|
|
1661
|
-
* frames the camera on the loci's bounding sphere and a lone atom would zoom
|
|
1662
|
-
* far closer than the residue warrants.
|
|
1663
|
-
*/
|
|
1664
|
-
function lociMatching(structure, matches) {
|
|
1665
|
-
const location = molstar_lib_mol_model_structure.StructureElement.Location.create(structure);
|
|
1666
|
-
const elements = [];
|
|
1667
|
-
for (const unit of structure.units) {
|
|
1668
|
-
location.unit = unit;
|
|
1669
|
-
const indices = [];
|
|
1670
|
-
for (let i = 0; i < unit.elements.length; i++) {
|
|
1671
|
-
location.element = unit.elements[i];
|
|
1672
|
-
if (matches(location)) indices.push(i);
|
|
1673
|
-
}
|
|
1674
|
-
if (indices.length > 0) elements.push({
|
|
1675
|
-
indices,
|
|
1676
|
-
unit
|
|
1677
|
-
});
|
|
1678
|
-
}
|
|
1679
|
-
if (elements.length === 0) return void 0;
|
|
1680
|
-
return molstar_lib_mol_model_structure.StructureElement.Loci(structure, elements.map(({ indices, unit }) => ({
|
|
1681
|
-
indices: molstar_lib_mol_data_int.OrderedSet.ofSortedArray(indices),
|
|
1682
|
-
unit
|
|
1683
|
-
})));
|
|
1684
|
-
}
|
|
1685
|
-
/**
|
|
1686
|
-
* Every atom of the residues a selection covers, within one structure.
|
|
1687
|
-
*
|
|
1688
|
-
* `hiddenChains` is subtracted from the result. A hidden chain is not drawn, so
|
|
1689
|
-
* geometry built from it would have nothing to sit on: Mol*'s focus
|
|
1690
|
-
* representation draws its own ball-and-stick from whatever is focused, in a
|
|
1691
|
-
* part of the state tree the chain's own visibility does not reach, and a
|
|
1692
|
-
* hidden chain left in the loci would keep showing up there after its cartoon
|
|
1693
|
-
* had gone.
|
|
1694
|
-
*/
|
|
1695
|
-
function lociForSelectionInStructure(structure, selection, hiddenChains) {
|
|
1696
|
-
var _selection$residues, _selection$chains;
|
|
1697
|
-
if (isEmptySelection(selection)) return void 0;
|
|
1698
|
-
const residues = new Set((_selection$residues = selection.residues) !== null && _selection$residues !== void 0 ? _selection$residues : []);
|
|
1699
|
-
const chains = new Set((_selection$chains = selection.chains) !== null && _selection$chains !== void 0 ? _selection$chains : []);
|
|
1700
|
-
return lociMatching(structure, (location) => {
|
|
1701
|
-
const chainId = molstar_lib_mol_model_structure.StructureProperties.chain.auth_asym_id(location);
|
|
1702
|
-
if (hiddenChains === null || hiddenChains === void 0 ? void 0 : hiddenChains.has(chainId)) return false;
|
|
1703
|
-
return residues.has(molstar_lib_mol_model_structure.StructureProperties.residue.key(location)) || chains.has(chainId);
|
|
1704
|
-
});
|
|
1705
|
-
}
|
|
1706
|
-
/**
|
|
1707
|
-
* Every atom of the residues between two 0-based indices, inclusive.
|
|
1708
|
-
*
|
|
1709
|
-
* A contiguous span is tested by comparison rather than by expanding it into a
|
|
1710
|
-
* set of indices: this runs on every residue a drag crosses, and a drag can
|
|
1711
|
-
* span the whole structure.
|
|
1712
|
-
*/
|
|
1713
|
-
function lociForResidueRange(structure, min, max) {
|
|
1714
|
-
return lociMatching(structure, (location) => {
|
|
1715
|
-
const index = molstar_lib_mol_model_structure.StructureProperties.residue.key(location);
|
|
1716
|
-
return index >= min && index <= max;
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
/**
|
|
1720
|
-
* The same, across every structure loaded into a plugin. Results are merged
|
|
1721
|
-
* into the first structure that matched: a loci belongs to one structure, and
|
|
1722
|
-
* the residue index space already spans them as though they were one.
|
|
1723
|
-
*/
|
|
1724
|
-
function lociForSelection(plugin, selection, hiddenChains) {
|
|
1725
|
-
for (const entry of plugin.managers.structure.hierarchy.current.structures) {
|
|
1726
|
-
var _entry$cell$obj;
|
|
1727
|
-
const structure = (_entry$cell$obj = entry.cell.obj) === null || _entry$cell$obj === void 0 ? void 0 : _entry$cell$obj.data;
|
|
1728
|
-
if (!structure) continue;
|
|
1729
|
-
const loci = lociForSelectionInStructure(structure, selection, hiddenChains);
|
|
1730
|
-
if (loci) return loci;
|
|
1731
|
-
}
|
|
1732
|
-
}
|
|
1733
|
-
//#endregion
|
|
1734
1597
|
//#region src/core/ProteinStructureViewer/utils/residueRef.ts
|
|
1735
1598
|
/**
|
|
1736
1599
|
* Reads the residue a loci points at, or null when it points at nothing the
|
|
@@ -1821,7 +1684,7 @@ function extendToRange(loci, anchor) {
|
|
|
1821
1684
|
if (from.index === to.index) return loci;
|
|
1822
1685
|
const min = Math.min(from.index, to.index);
|
|
1823
1686
|
const max = Math.max(from.index, to.index);
|
|
1824
|
-
return (_lociForResidueRange = lociForResidueRange(loci.structure, min, max)) !== null && _lociForResidueRange !== void 0 ? _lociForResidueRange : loci;
|
|
1687
|
+
return (_lociForResidueRange = require_renderStructureImage.lociForResidueRange(loci.structure, min, max)) !== null && _lociForResidueRange !== void 0 ? _lociForResidueRange : loci;
|
|
1825
1688
|
}
|
|
1826
1689
|
//#endregion
|
|
1827
1690
|
//#region src/core/ProteinStructureViewer/components/SequenceView/hooks/useHoveredResidue.ts
|
|
@@ -2459,138 +2322,6 @@ function createSequenceView(viewSettings) {
|
|
|
2459
2322
|
};
|
|
2460
2323
|
}
|
|
2461
2324
|
//#endregion
|
|
2462
|
-
//#region src/core/ProteinStructureViewer/utils/axes.ts
|
|
2463
|
-
/**
|
|
2464
|
-
* The orientation axes widget, as Mol* parameterizes it.
|
|
2465
|
-
*
|
|
2466
|
-
* Shared rather than declared where it is used, because two different parts of
|
|
2467
|
-
* Mol* take the same shape: the live canvas takes it as
|
|
2468
|
-
* `camera.helper.axes`, and the screenshot helper takes it as its own `axes`,
|
|
2469
|
-
* so an image can carry the widget or leave it out independently of the view
|
|
2470
|
-
* it was captured from.
|
|
2471
|
-
*/
|
|
2472
|
-
const AXES_ON = {
|
|
2473
|
-
name: "on",
|
|
2474
|
-
params: {
|
|
2475
|
-
alpha: .51,
|
|
2476
|
-
colorX: 16711680,
|
|
2477
|
-
colorY: 32768,
|
|
2478
|
-
colorZ: 255,
|
|
2479
|
-
labelColorX: 8421504,
|
|
2480
|
-
labelColorY: 8421504,
|
|
2481
|
-
labelColorZ: 8421504,
|
|
2482
|
-
labelOpacity: 1,
|
|
2483
|
-
labelScale: .25,
|
|
2484
|
-
labelX: "X",
|
|
2485
|
-
labelY: "Y",
|
|
2486
|
-
labelZ: "Z",
|
|
2487
|
-
location: "bottom-left",
|
|
2488
|
-
locationOffsetX: 0,
|
|
2489
|
-
locationOffsetY: 0,
|
|
2490
|
-
originColor: 8421504,
|
|
2491
|
-
planeColorXY: 8421504,
|
|
2492
|
-
planeColorXZ: 8421504,
|
|
2493
|
-
planeColorYZ: 8421504,
|
|
2494
|
-
radiusScale: .075,
|
|
2495
|
-
scale: .15,
|
|
2496
|
-
showLabels: false,
|
|
2497
|
-
showPlanes: true
|
|
2498
|
-
}
|
|
2499
|
-
};
|
|
2500
|
-
const AXES_OFF = {
|
|
2501
|
-
name: "off",
|
|
2502
|
-
params: {}
|
|
2503
|
-
};
|
|
2504
|
-
//#endregion
|
|
2505
|
-
//#region src/core/ProteinStructureViewer/utils/color.ts
|
|
2506
|
-
/** Parses `#RRGGBB` (or `RRGGBB`) into a Mol* `Color`. */
|
|
2507
|
-
function parseHexColor(hex) {
|
|
2508
|
-
const h = hex.replace("#", "");
|
|
2509
|
-
return molstar_lib_mol_util_color.Color.fromRgb(parseInt(h.substring(0, 2), 16), parseInt(h.substring(2, 4), 16), parseInt(h.substring(4, 6), 16));
|
|
2510
|
-
}
|
|
2511
|
-
/**
|
|
2512
|
-
* Parses a theme color into a Mol* `Color`, falling back when the token is
|
|
2513
|
-
* missing or is a format Mol* cannot consume (theme values are hex in practice,
|
|
2514
|
-
* but the palette types allow any CSS color string).
|
|
2515
|
-
*/
|
|
2516
|
-
function themeColor(value, fallback) {
|
|
2517
|
-
const candidate = value === null || value === void 0 ? void 0 : value.trim();
|
|
2518
|
-
return parseHexColor(candidate !== void 0 && /^#?[0-9a-f]{6}$/i.test(candidate) ? candidate : fallback);
|
|
2519
|
-
}
|
|
2520
|
-
/**
|
|
2521
|
-
* Neutral fill for residues with no overlay value. Sits between the light and
|
|
2522
|
-
* dark backgrounds so unhighlighted geometry stays legible in both modes.
|
|
2523
|
-
*/
|
|
2524
|
-
function neutralResidueColor(mode) {
|
|
2525
|
-
const g = mode === "dark" ? 100 : 180;
|
|
2526
|
-
return molstar_lib_mol_util_color.Color.fromRgb(g, g, g);
|
|
2527
|
-
}
|
|
2528
|
-
//#endregion
|
|
2529
|
-
//#region src/core/ProteinStructureViewer/utils/screenshot.ts
|
|
2530
|
-
/**
|
|
2531
|
-
* Each resolution as the Mol* preset that produces it: 1280x720, 1920x1080,
|
|
2532
|
-
* 3840x2160, 7680x4320. Named here rather than given as dimensions so the
|
|
2533
|
-
* aspect stays Mol*'s to choose.
|
|
2534
|
-
*
|
|
2535
|
-
* Mol*'s own `viewport` preset - the canvas at its current size - is left out
|
|
2536
|
-
* deliberately: it makes the image depend on how big the viewer happens to be
|
|
2537
|
-
* on screen, which is not something a caller asking for "high" is asking for.
|
|
2538
|
-
*/
|
|
2539
|
-
const RESOLUTION_PRESETS = {
|
|
2540
|
-
high: "ultra-hd",
|
|
2541
|
-
low: "hd",
|
|
2542
|
-
maximum: "8k-ultra-hd",
|
|
2543
|
-
medium: "full-hd"
|
|
2544
|
-
};
|
|
2545
|
-
/** Painted behind the structure when a background is asked for but unreadable. */
|
|
2546
|
-
const FALLBACK_BACKGROUND = "#ffffff";
|
|
2547
|
-
/**
|
|
2548
|
-
* Downloads the structure as a PNG.
|
|
2549
|
-
*
|
|
2550
|
-
* Mol*'s screenshot helper renders its own pass rather than reading the canvas
|
|
2551
|
-
* back, so the image is drawn fresh at the requested size and carries its own
|
|
2552
|
-
* transparency and axes settings - none of which disturb the view on screen.
|
|
2553
|
-
*
|
|
2554
|
-
* A named background is the one exception. The helper only chooses between
|
|
2555
|
-
* transparent and whatever the renderer is already painting, so a color has to
|
|
2556
|
-
* be put on the canvas for the length of the capture and taken off afterwards.
|
|
2557
|
-
* `finally` is what guarantees the second half: leaving a figure's background
|
|
2558
|
-
* behind on the viewer would be a worse bug than a failed download.
|
|
2559
|
-
*/
|
|
2560
|
-
function downloadStructureImage(_x) {
|
|
2561
|
-
return _downloadStructureImage.apply(this, arguments);
|
|
2562
|
-
}
|
|
2563
|
-
function _downloadStructureImage() {
|
|
2564
|
-
_downloadStructureImage = _asyncToGenerator(function* (plugin, options = {}) {
|
|
2565
|
-
const helper = plugin.helpers.viewportScreenshot;
|
|
2566
|
-
if (!helper) return;
|
|
2567
|
-
const { backgroundColor, filename, resolution = "medium", showAxes } = options;
|
|
2568
|
-
const transparent = backgroundColor === void 0;
|
|
2569
|
-
helper.behaviors.values.next(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, helper.behaviors.values.value), {}, {
|
|
2570
|
-
axes: showAxes ? AXES_ON : AXES_OFF,
|
|
2571
|
-
format: {
|
|
2572
|
-
name: "png",
|
|
2573
|
-
params: {}
|
|
2574
|
-
},
|
|
2575
|
-
resolution: {
|
|
2576
|
-
name: RESOLUTION_PRESETS[resolution],
|
|
2577
|
-
params: {}
|
|
2578
|
-
},
|
|
2579
|
-
transparent
|
|
2580
|
-
}));
|
|
2581
|
-
const canvas3d = plugin.canvas3d;
|
|
2582
|
-
const painted = canvas3d === null || canvas3d === void 0 ? void 0 : canvas3d.props.renderer.backgroundColor;
|
|
2583
|
-
const overrideBackground = !transparent && canvas3d !== void 0;
|
|
2584
|
-
if (overrideBackground) canvas3d === null || canvas3d === void 0 || canvas3d.setProps({ renderer: { backgroundColor: themeColor(backgroundColor, FALLBACK_BACKGROUND) } });
|
|
2585
|
-
try {
|
|
2586
|
-
yield helper.download(filename);
|
|
2587
|
-
} finally {
|
|
2588
|
-
if (overrideBackground && painted !== void 0) canvas3d === null || canvas3d === void 0 || canvas3d.setProps({ renderer: { backgroundColor: painted } });
|
|
2589
|
-
}
|
|
2590
|
-
});
|
|
2591
|
-
return _downloadStructureImage.apply(this, arguments);
|
|
2592
|
-
}
|
|
2593
|
-
//#endregion
|
|
2594
2325
|
//#region src/core/ProteinStructureViewer/components/Viewport/constants.ts
|
|
2595
2326
|
/**
|
|
2596
2327
|
* Fraction of the bounding sphere radius within which the camera still counts
|
|
@@ -2693,7 +2424,7 @@ function useCameraReset() {
|
|
|
2693
2424
|
]);
|
|
2694
2425
|
return {
|
|
2695
2426
|
cameraChanged,
|
|
2696
|
-
resetCamera: (0, react.useCallback)(_asyncToGenerator(function* () {
|
|
2427
|
+
resetCamera: (0, react.useCallback)(require_renderStructureImage._asyncToGenerator(function* () {
|
|
2697
2428
|
yield molstar_lib_mol_plugin_commands.PluginCommands.Camera.Reset(plugin, { durationMs: 250 });
|
|
2698
2429
|
yield new Promise((resolve) => setTimeout(resolve, 250));
|
|
2699
2430
|
yield molstar_lib_mol_plugin_commands.PluginCommands.Camera.ResetAxes(plugin, { durationMs: 250 });
|
|
@@ -2753,22 +2484,32 @@ function createViewportView(viewSettings) {
|
|
|
2753
2484
|
return (_s$showAxes = s.showAxes) !== null && _s$showAxes !== void 0 ? _s$showAxes : true;
|
|
2754
2485
|
});
|
|
2755
2486
|
const download = useViewSetting(viewSettings, (s) => s.download);
|
|
2487
|
+
const onError = useViewSetting(viewSettings, (s) => s.onError);
|
|
2488
|
+
const sceneBusy = useViewSetting(viewSettings, (s) => {
|
|
2489
|
+
var _s$sceneBusy;
|
|
2490
|
+
return (_s$sceneBusy = s.sceneBusy) !== null && _s$sceneBusy !== void 0 ? _s$sceneBusy : false;
|
|
2491
|
+
});
|
|
2756
2492
|
/**
|
|
2757
2493
|
* Disabled while a capture is in flight. A high resolution takes long
|
|
2758
2494
|
* enough to click twice through, and each click renders its own pass.
|
|
2759
2495
|
*/
|
|
2760
2496
|
const [downloading, setDownloading] = (0, react.useState)(false);
|
|
2761
|
-
const captureImage = (0, react.useCallback)(_asyncToGenerator(function* () {
|
|
2497
|
+
const captureImage = (0, react.useCallback)(require_renderStructureImage._asyncToGenerator(function* () {
|
|
2762
2498
|
if (!download) return;
|
|
2763
2499
|
setDownloading(true);
|
|
2764
2500
|
try {
|
|
2765
|
-
yield
|
|
2501
|
+
yield require_renderStructureImage.exportStructureImage(plugin, download);
|
|
2766
2502
|
} catch (error) {
|
|
2767
|
-
|
|
2503
|
+
if (onError) onError(error, "capture");
|
|
2504
|
+
else console.error("Failed to download the structure image:", error);
|
|
2768
2505
|
} finally {
|
|
2769
2506
|
setDownloading(false);
|
|
2770
2507
|
}
|
|
2771
|
-
}), [
|
|
2508
|
+
}), [
|
|
2509
|
+
download,
|
|
2510
|
+
onError,
|
|
2511
|
+
plugin
|
|
2512
|
+
]);
|
|
2772
2513
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2773
2514
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(molstar_lib_mol_plugin_ui_viewport.Viewport, {}),
|
|
2774
2515
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -2802,7 +2543,7 @@ function createViewportView(viewSettings) {
|
|
|
2802
2543
|
textAlign: "left",
|
|
2803
2544
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_czi_sds_components.Button, {
|
|
2804
2545
|
"aria-label": "Download image of the structure",
|
|
2805
|
-
disabled: downloading,
|
|
2546
|
+
disabled: downloading || sceneBusy,
|
|
2806
2547
|
onClick: captureImage,
|
|
2807
2548
|
sdsStyle: "minimal",
|
|
2808
2549
|
sdsType: "secondary",
|
|
@@ -2909,150 +2650,6 @@ function syncClipToZoom(plugin, getClipRatio) {
|
|
|
2909
2650
|
}
|
|
2910
2651
|
}
|
|
2911
2652
|
//#endregion
|
|
2912
|
-
//#region src/core/ProteinStructureViewer/utils/chains.ts
|
|
2913
|
-
/**
|
|
2914
|
-
* The polymer of one chain, as named in the file. What each chain's cartoon is
|
|
2915
|
-
* built from, and so what makes a chain a thing Mol* can hide on its own.
|
|
2916
|
-
*
|
|
2917
|
-
* Paired with `chainLigandExpression`: between them they cover everything on a
|
|
2918
|
-
* chain the viewer draws, split by what it draws it as. The polymer
|
|
2919
|
-
* restriction is the one Mol*'s `polymer-cartoon` preset applied, and is what
|
|
2920
|
-
* keeps a heme from being handed to a representation that can only trace a
|
|
2921
|
-
* backbone.
|
|
2922
|
-
*/
|
|
2923
|
-
function chainPolymerExpression(chainId) {
|
|
2924
|
-
return molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.generator.atomGroups({
|
|
2925
|
-
"chain-test": molstar_lib_mol_script_language_builder.MolScriptBuilder.core.rel.eq([molstar_lib_mol_script_language_builder.MolScriptBuilder.ammp("auth_asym_id"), chainId]),
|
|
2926
|
-
"entity-test": molstar_lib_mol_script_language_builder.MolScriptBuilder.core.rel.eq([molstar_lib_mol_script_language_builder.MolScriptBuilder.ammp("entityType"), "polymer"])
|
|
2927
|
-
});
|
|
2928
|
-
}
|
|
2929
|
-
/**
|
|
2930
|
-
* Bonds worth following out of a ligand: the ones holding it to the protein.
|
|
2931
|
-
*
|
|
2932
|
-
* Mol*'s own pair, and metallic coordination is the half that matters most -
|
|
2933
|
-
* a heme's iron is held to its histidine by a coordination bond and not a
|
|
2934
|
-
* covalent one, so covalent alone would leave every metalloprotein's cofactor
|
|
2935
|
-
* looking dropped in rather than bound.
|
|
2936
|
-
*/
|
|
2937
|
-
const CONNECTING_BOND = molstar_lib_mol_script_language_builder.MolScriptBuilder.core.flags.hasAny([molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.bondProperty.flags(), molstar_lib_mol_script_language_builder.MolScriptBuilder.core.type.bitflags([molstar_lib_mol_model_structure_model_types.BondType.fromNames(["covalent", "metal-coordination"])])]);
|
|
2938
|
-
/**
|
|
2939
|
-
* The heteroatoms of one chain - ligands, ions, glycans, lipids - along with
|
|
2940
|
-
* the residues holding them, which is what its ball-and-stick is built from.
|
|
2941
|
-
*
|
|
2942
|
-
* `non-polymer` is the one entity type a PDB's HETATM records land under,
|
|
2943
|
-
* ligands and ions alike, so there is nothing finer to test here: the
|
|
2944
|
-
* distinction Mol*'s presets draw between their `ligand`, `ion` and `lipid`
|
|
2945
|
-
* components is a residue-name lookup made to give each a slightly different
|
|
2946
|
-
* style, and all three are sticks. `branched` is the separate type glycans
|
|
2947
|
-
* arrive as.
|
|
2948
|
-
*
|
|
2949
|
-
* Water is the third type and is left out of the seed. Every solvent molecule
|
|
2950
|
-
* drawn as sticks buries the structure they surround, which is why Mol* draws
|
|
2951
|
-
* them at 60% alpha rather than plain, and none of them is what "show the
|
|
2952
|
-
* ligands" asks for.
|
|
2953
|
-
*
|
|
2954
|
-
* One layer of bonds out from there, taken as whole residues, is what brings
|
|
2955
|
-
* the binding residue along - myoglobin's His93, say. Without it the heme
|
|
2956
|
-
* floats unattached in the middle of a cartoon it is visibly bonded to, and
|
|
2957
|
-
* the bond drawn to its iron ends in mid-air. One layer and no more: the
|
|
2958
|
-
* residue that holds the ligand, not that residue's own neighbours.
|
|
2959
|
-
*
|
|
2960
|
-
* Intersected back with the chain, so everything a chain is drawn with belongs
|
|
2961
|
-
* to that chain. A ligand bonded across an interface would otherwise leave the
|
|
2962
|
-
* partner chain's residue in this chain's component, somewhere hiding the
|
|
2963
|
-
* partner chain could not reach.
|
|
2964
|
-
*/
|
|
2965
|
-
function chainLigandExpression(chainId) {
|
|
2966
|
-
const onThisChain = molstar_lib_mol_script_language_builder.MolScriptBuilder.core.rel.eq([molstar_lib_mol_script_language_builder.MolScriptBuilder.ammp("auth_asym_id"), chainId]);
|
|
2967
|
-
return molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.modifier.union([molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.modifier.intersectBy({
|
|
2968
|
-
0: molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.modifier.includeConnected({
|
|
2969
|
-
0: molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.generator.atomGroups({
|
|
2970
|
-
"chain-test": onThisChain,
|
|
2971
|
-
"entity-test": molstar_lib_mol_script_language_builder.MolScriptBuilder.core.set.has([molstar_lib_mol_script_language_builder.MolScriptBuilder.set("non-polymer", "branched"), molstar_lib_mol_script_language_builder.MolScriptBuilder.ammp("entityType")])
|
|
2972
|
-
}),
|
|
2973
|
-
"as-whole-residues": true,
|
|
2974
|
-
"bond-test": CONNECTING_BOND,
|
|
2975
|
-
"layer-count": 1
|
|
2976
|
-
}),
|
|
2977
|
-
by: molstar_lib_mol_script_language_builder.MolScriptBuilder.struct.generator.atomGroups({ "chain-test": onThisChain })
|
|
2978
|
-
})]);
|
|
2979
|
-
}
|
|
2980
|
-
/**
|
|
2981
|
-
* Matches how the sequence panel captions its chains, which reaches the same
|
|
2982
|
-
* label through Mol*'s own `getChainOptions`. Deriving it the same way is what
|
|
2983
|
-
* keeps a chain from going by one name in the legend and another in the panel.
|
|
2984
|
-
*/
|
|
2985
|
-
const CHAIN_LABEL_OPTIONS = {
|
|
2986
|
-
granularity: "chain",
|
|
2987
|
-
hidePrefix: true,
|
|
2988
|
-
htmlStyling: false
|
|
2989
|
-
};
|
|
2990
|
-
/**
|
|
2991
|
-
* Reads the chains of a structure and the polymer residues on each.
|
|
2992
|
-
*
|
|
2993
|
-
* Residue indices are Mol*'s residue keys, counting residues in file order
|
|
2994
|
-
* across the whole structure, so what is reported here addresses the same
|
|
2995
|
-
* residues as `plddt`, `residueOverlay` and `selection`.
|
|
2996
|
-
*
|
|
2997
|
-
* Heteroatoms are drawn (see `chainLigandExpression`) but are not counted
|
|
2998
|
-
* here. A heme is a residue to the file and not to a reader: it is no part of
|
|
2999
|
-
* the sequence the panel lists, and averaging a confidence score over it would
|
|
3000
|
-
* divide by a residue that has none.
|
|
3001
|
-
*
|
|
3002
|
-
* Residues are gathered into a set rather than inferred from the ends: a chain
|
|
3003
|
-
* carrying several symmetry operators repeats each of its residues once per
|
|
3004
|
-
* operator, and a structure whose chains interleave leaves a chain's indices
|
|
3005
|
-
* non-contiguous. Neither is what a predicted complex looks like, but a range
|
|
3006
|
-
* would misread both.
|
|
3007
|
-
*/
|
|
3008
|
-
function scanChains(structure) {
|
|
3009
|
-
const accumulators = /* @__PURE__ */ new Map();
|
|
3010
|
-
const location = molstar_lib_mol_model_structure.StructureElement.Location.create(structure);
|
|
3011
|
-
for (const unit of structure.units) {
|
|
3012
|
-
location.unit = unit;
|
|
3013
|
-
for (let i = 0; i < unit.elements.length; i++) {
|
|
3014
|
-
location.element = unit.elements[i];
|
|
3015
|
-
const chainId = molstar_lib_mol_model_structure.StructureProperties.chain.auth_asym_id(location);
|
|
3016
|
-
let chain = accumulators.get(chainId);
|
|
3017
|
-
if (!chain) {
|
|
3018
|
-
chain = {
|
|
3019
|
-
chainId,
|
|
3020
|
-
label: (0, molstar_lib_mol_theme_label.elementLabel)(location, CHAIN_LABEL_OPTIONS),
|
|
3021
|
-
residues: /* @__PURE__ */ new Set()
|
|
3022
|
-
};
|
|
3023
|
-
accumulators.set(chainId, chain);
|
|
3024
|
-
}
|
|
3025
|
-
if (molstar_lib_mol_model_structure.StructureProperties.entity.type(location) !== "polymer") continue;
|
|
3026
|
-
chain.residues.add(molstar_lib_mol_model_structure.StructureProperties.residue.key(location));
|
|
3027
|
-
}
|
|
3028
|
-
}
|
|
3029
|
-
const ordered = [...accumulators.values()].filter((chain) => chain.residues.size > 0).map((chain) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, chain), {}, { residues: [...chain.residues].sort((a, b) => a - b) })).sort((a, b) => {
|
|
3030
|
-
var _a$residues$, _b$residues$;
|
|
3031
|
-
return ((_a$residues$ = a.residues[0]) !== null && _a$residues$ !== void 0 ? _a$residues$ : 0) - ((_b$residues$ = b.residues[0]) !== null && _b$residues$ !== void 0 ? _b$residues$ : 0);
|
|
3032
|
-
});
|
|
3033
|
-
return {
|
|
3034
|
-
chainLabels: new Map([...accumulators.values()].map(({ chainId, label }) => [chainId, label])),
|
|
3035
|
-
chains: ordered.map(({ chainId, label, residues }) => {
|
|
3036
|
-
var _residues, _residues$;
|
|
3037
|
-
return {
|
|
3038
|
-
chainId,
|
|
3039
|
-
endIndex: (_residues = residues[residues.length - 1]) !== null && _residues !== void 0 ? _residues : 0,
|
|
3040
|
-
label,
|
|
3041
|
-
residueCount: residues.length,
|
|
3042
|
-
startIndex: (_residues$ = residues[0]) !== null && _residues$ !== void 0 ? _residues$ : 0
|
|
3043
|
-
};
|
|
3044
|
-
}),
|
|
3045
|
-
residuesByChain: new Map(ordered.map(({ chainId, residues }) => [chainId, residues]))
|
|
3046
|
-
};
|
|
3047
|
-
}
|
|
3048
|
-
/** True when the two lists name the same chains, in the same order. */
|
|
3049
|
-
function chainsEqual(a, b) {
|
|
3050
|
-
return a.length === b.length && a.every((chain, i) => {
|
|
3051
|
-
const other = b[i];
|
|
3052
|
-
return other !== void 0 && chain.chainId === other.chainId && chain.startIndex === other.startIndex && chain.endIndex === other.endIndex && chain.residueCount === other.residueCount;
|
|
3053
|
-
});
|
|
3054
|
-
}
|
|
3055
|
-
//#endregion
|
|
3056
2653
|
//#region src/core/ProteinStructureViewer/utils/molstarSpec.ts
|
|
3057
2654
|
/**
|
|
3058
2655
|
* The spec's list-valued keys. These are appended rather than merged: Mol*
|
|
@@ -3120,421 +2717,16 @@ function mergeMolstarSpec(base, override) {
|
|
|
3120
2717
|
return merged;
|
|
3121
2718
|
}
|
|
3122
2719
|
//#endregion
|
|
3123
|
-
//#region src/core/ProteinStructureViewer/utils/structureFormat.ts
|
|
3124
|
-
/** How much of the file to inspect when guessing the format. */
|
|
3125
|
-
const DETECT_SAMPLE = 16384;
|
|
3126
|
-
/**
|
|
3127
|
-
* Infers whether `text` is PDB or mmCIF (PDBx).
|
|
3128
|
-
*
|
|
3129
|
-
* mmCIF documents start with a `data_` block and name their coordinates
|
|
3130
|
-
* `_atom_site.`. PDB never does either, so either marker is enough. A leading
|
|
3131
|
-
* comment (`# ...`) before `data_` is still mmCIF.
|
|
3132
|
-
*/
|
|
3133
|
-
function detectStructureFormat(text) {
|
|
3134
|
-
const sample = text.slice(0, DETECT_SAMPLE);
|
|
3135
|
-
if (/(?:^|\n)\s*data_/.test(sample) || /_atom_site\./.test(sample)) return "mmcif";
|
|
3136
|
-
return "pdb";
|
|
3137
|
-
}
|
|
3138
|
-
//#endregion
|
|
3139
|
-
//#region src/core/ProteinStructureViewer/utils/chainColorTheme.ts
|
|
3140
|
-
/** Mol* color theme name under which per-chain coloring is registered. */
|
|
3141
|
-
const CHAIN_COLOR_THEME_NAME = "chain-color";
|
|
3142
|
-
/** Painted for a chain with no color assigned, or one assigned a bad value. */
|
|
3143
|
-
const DEFAULT_CHAIN_COLOR_HEX = "#808080";
|
|
3144
|
-
const DEFAULT_CHAIN_COLOR = molstar_lib_mol_util_color.Color.fromRgb(128, 128, 128);
|
|
3145
|
-
/**
|
|
3146
|
-
* Builds a Mol* color theme that paints each chain its own color.
|
|
3147
|
-
*
|
|
3148
|
-
* This stands in for Mol*'s built-in `chain-id` theme, which picks its own
|
|
3149
|
-
* colors from a palette we cannot read back. Owning the assignment is what
|
|
3150
|
-
* lets the chain legend show a swatch that matches the structure, rather than
|
|
3151
|
-
* a key for colors it has to guess at.
|
|
3152
|
-
*
|
|
3153
|
-
* Created per plugin instance, and its state read live inside `color()`, for
|
|
3154
|
-
* the same reasons as the residue value theme beside it: several viewers can
|
|
3155
|
-
* color chains differently on one page, and recoloring does not rebuild the
|
|
3156
|
-
* structure.
|
|
3157
|
-
*/
|
|
3158
|
-
function createChainColorTheme() {
|
|
3159
|
-
const state = {
|
|
3160
|
-
colors: /* @__PURE__ */ new Map(),
|
|
3161
|
-
version: 0
|
|
3162
|
-
};
|
|
3163
|
-
const provider = {
|
|
3164
|
-
category: "chain property",
|
|
3165
|
-
defaultValues: {},
|
|
3166
|
-
factory() {
|
|
3167
|
-
const { colors } = state;
|
|
3168
|
-
const parsed = /* @__PURE__ */ new Map();
|
|
3169
|
-
for (const [chainId, hex] of colors) parsed.set(chainId, themeColor(hex, DEFAULT_CHAIN_COLOR_HEX));
|
|
3170
|
-
return {
|
|
3171
|
-
color(location) {
|
|
3172
|
-
var _parsed$get;
|
|
3173
|
-
if (!molstar_lib_mol_model_structure.StructureElement.Location.is(location)) return DEFAULT_CHAIN_COLOR;
|
|
3174
|
-
const chainId = molstar_lib_mol_model_structure.StructureProperties.chain.auth_asym_id(location);
|
|
3175
|
-
return (_parsed$get = parsed.get(chainId)) !== null && _parsed$get !== void 0 ? _parsed$get : DEFAULT_CHAIN_COLOR;
|
|
3176
|
-
},
|
|
3177
|
-
contextHash: state.version,
|
|
3178
|
-
description: "Color residues by the chain they sit on",
|
|
3179
|
-
factory: provider.factory,
|
|
3180
|
-
granularity: "group",
|
|
3181
|
-
props: {}
|
|
3182
|
-
};
|
|
3183
|
-
},
|
|
3184
|
-
getParams: () => ({}),
|
|
3185
|
-
isApplicable: () => true,
|
|
3186
|
-
label: "Chain Color",
|
|
3187
|
-
name: CHAIN_COLOR_THEME_NAME
|
|
3188
|
-
};
|
|
3189
|
-
return {
|
|
3190
|
-
provider,
|
|
3191
|
-
setState(colors) {
|
|
3192
|
-
state.colors = colors;
|
|
3193
|
-
state.version++;
|
|
3194
|
-
}
|
|
3195
|
-
};
|
|
3196
|
-
}
|
|
3197
|
-
//#endregion
|
|
3198
|
-
//#region src/core/ProteinStructureViewer/utils/plddt.ts
|
|
3199
|
-
/** Mol* color theme name under which pLDDT coloring is registered. */
|
|
3200
|
-
const PLDDT_THEME_NAME = "plddt-bfactor";
|
|
3201
|
-
/**
|
|
3202
|
-
* AlphaFold's pLDDT confidence bands. Thresholds are on the 0-1 scale that
|
|
3203
|
-
* `plddt` values use, ascending from lowest to highest confidence.
|
|
3204
|
-
*/
|
|
3205
|
-
const PLDDT_COLOR_SCALE = {
|
|
3206
|
-
kind: "stepped",
|
|
3207
|
-
stops: [
|
|
3208
|
-
{
|
|
3209
|
-
color: "#FF7C45",
|
|
3210
|
-
label: "0.5",
|
|
3211
|
-
threshold: .5
|
|
3212
|
-
},
|
|
3213
|
-
{
|
|
3214
|
-
color: "#FFDB11",
|
|
3215
|
-
label: "0.7",
|
|
3216
|
-
threshold: .7
|
|
3217
|
-
},
|
|
3218
|
-
{
|
|
3219
|
-
color: "#64CBF3",
|
|
3220
|
-
label: "0.9",
|
|
3221
|
-
threshold: .9
|
|
3222
|
-
},
|
|
3223
|
-
{
|
|
3224
|
-
color: "#0053D5",
|
|
3225
|
-
label: "1.0",
|
|
3226
|
-
threshold: 1
|
|
3227
|
-
}
|
|
3228
|
-
]
|
|
3229
|
-
};
|
|
3230
|
-
const PLDDT_BANDS = PLDDT_COLOR_SCALE.kind === "stepped" ? PLDDT_COLOR_SCALE.stops : [];
|
|
3231
|
-
/**
|
|
3232
|
-
* The band colors alone, ascending from least to most confident.
|
|
3233
|
-
*
|
|
3234
|
-
* What the chain legend shows in place of a single color while pLDDT is
|
|
3235
|
-
* painting the structure: no one color stands for a chain then, so the swatch
|
|
3236
|
-
* carries the whole key instead.
|
|
3237
|
-
*/
|
|
3238
|
-
const PLDDT_BAND_COLORS = PLDDT_BANDS.map((stop) => stop.color);
|
|
3239
|
-
const DEFAULT_PLDDT_COLOR = molstar_lib_mol_util_color.Color.fromRgb(128, 128, 128);
|
|
3240
|
-
/**
|
|
3241
|
-
* Maps a pLDDT confidence score on the 0-100 B-factor scale to its band color.
|
|
3242
|
-
* `PLDDT_COLOR_SCALE` thresholds are on the 0-1 scale, so the score is
|
|
3243
|
-
* normalized before it is sampled.
|
|
3244
|
-
*/
|
|
3245
|
-
function plddtToColor(value) {
|
|
3246
|
-
const [r, g, b] = require_colorScales.sampleSteppedScale(PLDDT_BANDS, value / 100);
|
|
3247
|
-
return molstar_lib_mol_util_color.Color.fromRgb(r, g, b);
|
|
3248
|
-
}
|
|
3249
|
-
/**
|
|
3250
|
-
* Rewrites a PDB file's B-factor column with pLDDT scores so Mol* can read them
|
|
3251
|
-
* off the parsed model. B-factors occupy columns 60-66 of ATOM/HETATM lines;
|
|
3252
|
-
* scores arrive on a 0-1 scale and are stored on the conventional 0-100 one.
|
|
3253
|
-
* Residues past the end of `plddtValues` fall back to a mid-confidence 50.
|
|
3254
|
-
*
|
|
3255
|
-
* A residue is identified by chain, sequence number and insertion code. The
|
|
3256
|
-
* code has to be part of that: `10` and `10A` are two residues, Mol* counts
|
|
3257
|
-
* them as two, and treating them as one here would hand every residue after
|
|
3258
|
-
* them its neighbour's score.
|
|
3259
|
-
*/
|
|
3260
|
-
function injectPlddtIntoPdb(pdbData, plddtValues) {
|
|
3261
|
-
const lines = pdbData.split("\n");
|
|
3262
|
-
let residueIndex = -1;
|
|
3263
|
-
let lastResidue = "";
|
|
3264
|
-
return lines.map((line) => {
|
|
3265
|
-
var _plddtValues$residueI;
|
|
3266
|
-
if (!line.startsWith("ATOM") && !line.startsWith("HETATM")) return line;
|
|
3267
|
-
const residue = line.substring(21, 27);
|
|
3268
|
-
if (residue !== lastResidue) {
|
|
3269
|
-
residueIndex++;
|
|
3270
|
-
lastResidue = residue;
|
|
3271
|
-
}
|
|
3272
|
-
const bfactorStr = (residueIndex < plddtValues.length ? ((_plddtValues$residueI = plddtValues[residueIndex]) !== null && _plddtValues$residueI !== void 0 ? _plddtValues$residueI : 0) * 100 : 50).toFixed(2).padStart(6, " ");
|
|
3273
|
-
const paddedLine = line.padEnd(66, " ");
|
|
3274
|
-
return paddedLine.substring(0, 60) + bfactorStr + paddedLine.substring(66);
|
|
3275
|
-
}).join("\n");
|
|
3276
|
-
}
|
|
3277
|
-
const ATOM_SITE_PREFIX = "_atom_site.";
|
|
3278
|
-
const B_FACTOR_COLUMN = "B_iso_or_equiv";
|
|
3279
|
-
function skipCifWhitespace(line, index) {
|
|
3280
|
-
let i = index;
|
|
3281
|
-
while (i < line.length && /\s/.test(line.charAt(i))) i += 1;
|
|
3282
|
-
return i;
|
|
3283
|
-
}
|
|
3284
|
-
function readCifToken(line, start) {
|
|
3285
|
-
const quote = line.charAt(start);
|
|
3286
|
-
if (quote === "'" || quote === "\"") {
|
|
3287
|
-
const end = line.indexOf(quote, start + 1);
|
|
3288
|
-
if (end < 0) return {
|
|
3289
|
-
next: line.length,
|
|
3290
|
-
token: line.slice(start + 1)
|
|
3291
|
-
};
|
|
3292
|
-
return {
|
|
3293
|
-
next: end + 1,
|
|
3294
|
-
token: line.slice(start + 1, end)
|
|
3295
|
-
};
|
|
3296
|
-
}
|
|
3297
|
-
let i = start;
|
|
3298
|
-
while (i < line.length && !/\s/.test(line.charAt(i))) i += 1;
|
|
3299
|
-
return {
|
|
3300
|
-
next: i,
|
|
3301
|
-
token: line.slice(start, i)
|
|
3302
|
-
};
|
|
3303
|
-
}
|
|
3304
|
-
/**
|
|
3305
|
-
* Splits one CIF line into tokens, honouring `'quoted'` and `"quoted"` values.
|
|
3306
|
-
* Atom-site rows are one line each in practice; multiline `;text;` fields are
|
|
3307
|
-
* not used there and are not handled.
|
|
3308
|
-
*/
|
|
3309
|
-
function tokenizeCifLine(line) {
|
|
3310
|
-
const tokens = [];
|
|
3311
|
-
let i = skipCifWhitespace(line, 0);
|
|
3312
|
-
while (i < line.length) {
|
|
3313
|
-
const { next, token } = readCifToken(line, i);
|
|
3314
|
-
tokens.push(token);
|
|
3315
|
-
i = skipCifWhitespace(line, next);
|
|
3316
|
-
}
|
|
3317
|
-
return tokens;
|
|
3318
|
-
}
|
|
3319
|
-
function isCifNull(value) {
|
|
3320
|
-
return value === void 0 || value === "." || value === "?";
|
|
3321
|
-
}
|
|
3322
|
-
function columnIndex(columns, name) {
|
|
3323
|
-
return columns.indexOf(name);
|
|
3324
|
-
}
|
|
3325
|
-
/**
|
|
3326
|
-
* Residue identity matching `injectPlddtIntoPdb`: chain + sequence number +
|
|
3327
|
-
* insertion code. Prefers the `auth_*` columns Mol* reports on the parsed
|
|
3328
|
-
* model, falling back to `label_*` when a file omitted them. `.` / `?`
|
|
3329
|
-
* insertion codes count as empty, the way a blank PDB column 27 does.
|
|
3330
|
-
*/
|
|
3331
|
-
function mmcifResidueKey(tokens, columns) {
|
|
3332
|
-
var _ref, _ref2, _ref3, _ref4;
|
|
3333
|
-
const authAsym = columnIndex(columns, "auth_asym_id");
|
|
3334
|
-
const labelAsym = columnIndex(columns, "label_asym_id");
|
|
3335
|
-
const authSeq = columnIndex(columns, "auth_seq_id");
|
|
3336
|
-
const labelSeq = columnIndex(columns, "label_seq_id");
|
|
3337
|
-
const ins = columnIndex(columns, "pdbx_PDB_ins_code");
|
|
3338
|
-
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]}`;
|
|
3339
|
-
}
|
|
3340
|
-
function isAtomSiteRow(tokens, columns) {
|
|
3341
|
-
const group = columnIndex(columns, "group_PDB");
|
|
3342
|
-
if (group < 0) return true;
|
|
3343
|
-
const value = tokens[group];
|
|
3344
|
-
return value === "ATOM" || value === "HETATM";
|
|
3345
|
-
}
|
|
3346
|
-
function plddtBFactor(residueIndex, plddtValues) {
|
|
3347
|
-
var _plddtValues$residueI2;
|
|
3348
|
-
return (residueIndex < plddtValues.length ? ((_plddtValues$residueI2 = plddtValues[residueIndex]) !== null && _plddtValues$residueI2 !== void 0 ? _plddtValues$residueI2 : 0) * 100 : 50).toFixed(2);
|
|
3349
|
-
}
|
|
3350
|
-
function isLoopStart(line) {
|
|
3351
|
-
return line.trim() === "loop_" || line.trim().startsWith("loop_");
|
|
3352
|
-
}
|
|
3353
|
-
function isAtomSiteColumn(line) {
|
|
3354
|
-
return line.trim().startsWith(ATOM_SITE_PREFIX);
|
|
3355
|
-
}
|
|
3356
|
-
function endsAtomSiteLoop(line) {
|
|
3357
|
-
const trimmed = line.trim();
|
|
3358
|
-
if (trimmed === "") return false;
|
|
3359
|
-
if (trimmed.startsWith("#")) return true;
|
|
3360
|
-
if (isLoopStart(trimmed)) return true;
|
|
3361
|
-
if (trimmed.startsWith("data_") || trimmed.startsWith("save_")) return true;
|
|
3362
|
-
return trimmed.startsWith("_") && !trimmed.startsWith(ATOM_SITE_PREFIX);
|
|
3363
|
-
}
|
|
3364
|
-
function isAtomSiteLoop(lines, loopIndex) {
|
|
3365
|
-
let peek = loopIndex + 1;
|
|
3366
|
-
while (peek < lines.length && lines[peek].trim() === "") peek += 1;
|
|
3367
|
-
return peek < lines.length && isAtomSiteColumn(lines[peek]);
|
|
3368
|
-
}
|
|
3369
|
-
function ensureBFactorColumn(columns, headerLines) {
|
|
3370
|
-
const existing = columnIndex(columns, B_FACTOR_COLUMN);
|
|
3371
|
-
if (existing >= 0) return existing;
|
|
3372
|
-
columns.push(B_FACTOR_COLUMN);
|
|
3373
|
-
headerLines.push(`${ATOM_SITE_PREFIX}${B_FACTOR_COLUMN}`);
|
|
3374
|
-
return columns.length - 1;
|
|
3375
|
-
}
|
|
3376
|
-
function rewriteAtomSiteRow(dataLine, columns, bIndex, plddtValues, walk) {
|
|
3377
|
-
if (dataLine.trim() === "") return dataLine;
|
|
3378
|
-
const tokens = tokenizeCifLine(dataLine);
|
|
3379
|
-
if (tokens.length === 0) return dataLine;
|
|
3380
|
-
while (tokens.length < columns.length) tokens.push(".");
|
|
3381
|
-
if (isAtomSiteRow(tokens, columns)) {
|
|
3382
|
-
const residue = mmcifResidueKey(tokens, columns);
|
|
3383
|
-
if (residue !== walk.lastResidue) {
|
|
3384
|
-
walk.residueIndex += 1;
|
|
3385
|
-
walk.lastResidue = residue;
|
|
3386
|
-
}
|
|
3387
|
-
tokens[bIndex] = plddtBFactor(walk.residueIndex, plddtValues);
|
|
3388
|
-
}
|
|
3389
|
-
return tokens.join(" ");
|
|
3390
|
-
}
|
|
3391
|
-
function rewriteAtomSiteLoop(lines, loopIndex, plddtValues, walk) {
|
|
3392
|
-
const rows = [lines[loopIndex]];
|
|
3393
|
-
let i = loopIndex + 1;
|
|
3394
|
-
const columns = [];
|
|
3395
|
-
while (i < lines.length && isAtomSiteColumn(lines[i])) {
|
|
3396
|
-
columns.push(lines[i].trim().slice(11));
|
|
3397
|
-
rows.push(lines[i]);
|
|
3398
|
-
i += 1;
|
|
3399
|
-
}
|
|
3400
|
-
const bIndex = ensureBFactorColumn(columns, rows);
|
|
3401
|
-
while (i < lines.length && !endsAtomSiteLoop(lines[i])) {
|
|
3402
|
-
rows.push(rewriteAtomSiteRow(lines[i], columns, bIndex, plddtValues, walk));
|
|
3403
|
-
i += 1;
|
|
3404
|
-
}
|
|
3405
|
-
return {
|
|
3406
|
-
next: i,
|
|
3407
|
-
rows
|
|
3408
|
-
};
|
|
3409
|
-
}
|
|
3410
|
-
/**
|
|
3411
|
-
* Rewrites an mmCIF (PDBx) file's `_atom_site.B_iso_or_equiv` column with
|
|
3412
|
-
* pLDDT scores so Mol* can read them off the parsed model, the same way
|
|
3413
|
-
* `injectPlddtIntoPdb` rewrites the PDB B-factor column. Scores arrive on a
|
|
3414
|
-
* 0-1 scale and are stored on the conventional 0-100 one. Residues past the
|
|
3415
|
-
* end of `plddtValues` fall back to a mid-confidence 50.
|
|
3416
|
-
*
|
|
3417
|
-
* If the file has no B-factor column, one is appended to the atom-site loop
|
|
3418
|
-
* rather than leaving the scores behind.
|
|
3419
|
-
*/
|
|
3420
|
-
function injectPlddtIntoMmcif(mmcifData, plddtValues) {
|
|
3421
|
-
const lines = mmcifData.split("\n");
|
|
3422
|
-
const result = [];
|
|
3423
|
-
const walk = {
|
|
3424
|
-
lastResidue: "",
|
|
3425
|
-
residueIndex: -1
|
|
3426
|
-
};
|
|
3427
|
-
let i = 0;
|
|
3428
|
-
let rewrittenLoop = false;
|
|
3429
|
-
while (i < lines.length) {
|
|
3430
|
-
const line = lines[i];
|
|
3431
|
-
if (!rewrittenLoop && isLoopStart(line) && isAtomSiteLoop(lines, i)) {
|
|
3432
|
-
const rewritten = rewriteAtomSiteLoop(lines, i, plddtValues, walk);
|
|
3433
|
-
result.push(...rewritten.rows);
|
|
3434
|
-
i = rewritten.next;
|
|
3435
|
-
rewrittenLoop = true;
|
|
3436
|
-
continue;
|
|
3437
|
-
}
|
|
3438
|
-
result.push(line);
|
|
3439
|
-
i += 1;
|
|
3440
|
-
}
|
|
3441
|
-
return result.join("\n");
|
|
3442
|
-
}
|
|
3443
|
-
/**
|
|
3444
|
-
* Rewrites a structure file's B-factors with pLDDT scores. Dispatches to
|
|
3445
|
-
* `injectPlddtIntoPdb` or `injectPlddtIntoMmcif` from `format`, or from
|
|
3446
|
-
* detecting the format of `data` when `format` is omitted.
|
|
3447
|
-
*/
|
|
3448
|
-
function injectPlddt(data, plddtValues, format = detectStructureFormat(data)) {
|
|
3449
|
-
return format === "mmcif" ? injectPlddtIntoMmcif(data, plddtValues) : injectPlddtIntoPdb(data, plddtValues);
|
|
3450
|
-
}
|
|
3451
|
-
/**
|
|
3452
|
-
* Mol* color theme that reads pLDDT scores out of the B-factor column, paired
|
|
3453
|
-
* with `injectPlddt`. Registered per plugin instance.
|
|
3454
|
-
*/
|
|
3455
|
-
const PlddtColoring = molstar_lib_mol_model_props_common_custom_element_property.CustomElementProperty.create({
|
|
3456
|
-
coloring: {
|
|
3457
|
-
defaultColor: DEFAULT_PLDDT_COLOR,
|
|
3458
|
-
getColor(e) {
|
|
3459
|
-
return plddtToColor(e);
|
|
3460
|
-
}
|
|
3461
|
-
},
|
|
3462
|
-
getData(model) {
|
|
3463
|
-
const map = /* @__PURE__ */ new Map();
|
|
3464
|
-
const atomCount = model.atomicHierarchy.atoms._rowCount;
|
|
3465
|
-
for (let i = 0; i < atomCount; i++) map.set(i, model.atomicConformation.B_iso_or_equiv.value(i));
|
|
3466
|
-
return { value: map };
|
|
3467
|
-
},
|
|
3468
|
-
label: "pLDDT",
|
|
3469
|
-
name: PLDDT_THEME_NAME
|
|
3470
|
-
});
|
|
3471
|
-
//#endregion
|
|
3472
|
-
//#region src/core/ProteinStructureViewer/utils/residueValueTheme.ts
|
|
3473
|
-
/** Mol* color theme name under which residue value overlays are registered. */
|
|
3474
|
-
const RESIDUE_VALUE_THEME_NAME = "residue-value";
|
|
3475
|
-
/**
|
|
3476
|
-
* Builds a Mol* color theme that paints residues by an arbitrary per-residue
|
|
3477
|
-
* value.
|
|
3478
|
-
*
|
|
3479
|
-
* The theme is created per plugin instance rather than shared at module scope,
|
|
3480
|
-
* so several viewers can render different overlays on the same page. Its state
|
|
3481
|
-
* lives in the closure below and is read live inside `color()`, which is what
|
|
3482
|
-
* lets an overlay update recolor the structure without rebuilding it.
|
|
3483
|
-
*/
|
|
3484
|
-
function createResidueValueTheme(mode) {
|
|
3485
|
-
const state = {
|
|
3486
|
-
colorScale: require_colorScales.PLASMA_COLOR_SCALE,
|
|
3487
|
-
max: 1,
|
|
3488
|
-
min: 0,
|
|
3489
|
-
mode,
|
|
3490
|
-
values: /* @__PURE__ */ new Map(),
|
|
3491
|
-
version: 0
|
|
3492
|
-
};
|
|
3493
|
-
const provider = {
|
|
3494
|
-
category: "residue property",
|
|
3495
|
-
defaultValues: {},
|
|
3496
|
-
factory() {
|
|
3497
|
-
const { colorScale, max, min, mode: currentMode, values } = state;
|
|
3498
|
-
const neutral = neutralResidueColor(currentMode);
|
|
3499
|
-
return {
|
|
3500
|
-
color(location) {
|
|
3501
|
-
if (!molstar_lib_mol_model_structure.StructureElement.Location.is(location)) return neutral;
|
|
3502
|
-
const value = values.get(molstar_lib_mol_model_structure.StructureProperties.residue.key(location));
|
|
3503
|
-
if (value === void 0) return neutral;
|
|
3504
|
-
const rgb = require_colorScales.sampleColorScale(colorScale, value, max, min);
|
|
3505
|
-
return rgb ? molstar_lib_mol_util_color.Color.fromRgb(rgb[0], rgb[1], rgb[2]) : neutral;
|
|
3506
|
-
},
|
|
3507
|
-
contextHash: state.version,
|
|
3508
|
-
description: "Color residues by a per-residue value",
|
|
3509
|
-
factory: provider.factory,
|
|
3510
|
-
granularity: "group",
|
|
3511
|
-
props: {}
|
|
3512
|
-
};
|
|
3513
|
-
},
|
|
3514
|
-
getParams: () => ({}),
|
|
3515
|
-
isApplicable: () => true,
|
|
3516
|
-
label: "Residue Value",
|
|
3517
|
-
name: RESIDUE_VALUE_THEME_NAME
|
|
3518
|
-
};
|
|
3519
|
-
return {
|
|
3520
|
-
provider,
|
|
3521
|
-
setState(next) {
|
|
3522
|
-
Object.assign(state, next);
|
|
3523
|
-
state.version++;
|
|
3524
|
-
}
|
|
3525
|
-
};
|
|
3526
|
-
}
|
|
3527
|
-
//#endregion
|
|
3528
2720
|
//#region src/core/ProteinStructureViewer/hooks/useMolstarPlugin.ts
|
|
3529
|
-
/**
|
|
3530
|
-
* Theme used when neither pLDDT scores nor an overlay are supplied. Our own
|
|
3531
|
-
* per-chain theme rather than Mol*'s built-in `chain-id`, so the chain legend
|
|
3532
|
-
* can show swatches that match the structure instead of guessing at the colors
|
|
3533
|
-
* Mol* picked.
|
|
3534
|
-
*/
|
|
3535
|
-
const FALLBACK_THEME_NAME = CHAIN_COLOR_THEME_NAME;
|
|
3536
2721
|
/** Delay before retrying initialization while the container has no size. */
|
|
3537
2722
|
const LAYOUT_RETRY_MS = 100;
|
|
2723
|
+
/** What the viewer logs for a failure when no `onError` is listening. */
|
|
2724
|
+
const UNHANDLED_ERROR_MESSAGES = {
|
|
2725
|
+
capture: "Failed to download the structure image:",
|
|
2726
|
+
init: "Failed to initialize Mol* viewer:",
|
|
2727
|
+
load: "Failed to load structure:",
|
|
2728
|
+
representation: "Failed to draw the structure:"
|
|
2729
|
+
};
|
|
3538
2730
|
/** How long to wait for a frame before starting without one. */
|
|
3539
2731
|
const FRAME_WAIT_MS = 100;
|
|
3540
2732
|
/**
|
|
@@ -3585,7 +2777,7 @@ const SCREENSHOT_CONTROLS_CONFIG = (() => {
|
|
|
3585
2777
|
function setAxes(plugin, enabled) {
|
|
3586
2778
|
if (!plugin.canvas3d) return;
|
|
3587
2779
|
try {
|
|
3588
|
-
plugin.canvas3d.setProps({ camera: { helper: { axes: enabled ? AXES_ON : AXES_OFF } } });
|
|
2780
|
+
plugin.canvas3d.setProps({ camera: { helper: { axes: enabled ? require_renderStructureImage.AXES_ON : require_renderStructureImage.AXES_OFF } } });
|
|
3589
2781
|
} catch (e) {
|
|
3590
2782
|
console.warn("Could not set axes:", e);
|
|
3591
2783
|
}
|
|
@@ -3594,7 +2786,7 @@ function createViewer(_x) {
|
|
|
3594
2786
|
return _createViewer.apply(this, arguments);
|
|
3595
2787
|
}
|
|
3596
2788
|
function _createViewer() {
|
|
3597
|
-
_createViewer = _asyncToGenerator(function* ({ backgroundColor,
|
|
2789
|
+
_createViewer = require_renderStructureImage._asyncToGenerator(function* ({ backgroundColor, edgeColor, highlightColor, molstarSpec, root, showAxes, showSequenceViewer, viewSettings }) {
|
|
3598
2790
|
var _spec$config;
|
|
3599
2791
|
const spec = (0, molstar_lib_mol_plugin_ui_spec.DefaultPluginUISpec)();
|
|
3600
2792
|
const behaviors = spec.behaviors.filter((b) => b.transformer !== molstar_lib_mol_plugin_behavior.PluginBehaviors.Camera.FocusLoci);
|
|
@@ -3603,7 +2795,7 @@ function _createViewer() {
|
|
|
3603
2795
|
spec: mergeMolstarSpec(require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, spec), {}, {
|
|
3604
2796
|
behaviors,
|
|
3605
2797
|
canvas3d: {
|
|
3606
|
-
camera: { helper: { axes: showAxes ? AXES_ON : AXES_OFF } },
|
|
2798
|
+
camera: { helper: { axes: showAxes ? require_renderStructureImage.AXES_ON : require_renderStructureImage.AXES_OFF } },
|
|
3607
2799
|
marking: {
|
|
3608
2800
|
highlightEdgeColor: edgeColor,
|
|
3609
2801
|
selectEdgeColor: edgeColor
|
|
@@ -3649,10 +2841,6 @@ function _createViewer() {
|
|
|
3649
2841
|
}), molstarSpec),
|
|
3650
2842
|
target: root
|
|
3651
2843
|
});
|
|
3652
|
-
const registry = plugin.representation.structure.themes.colorThemeRegistry;
|
|
3653
|
-
if (PlddtColoring.colorThemeProvider) registry.add(PlddtColoring.colorThemeProvider);
|
|
3654
|
-
registry.add(residueValueTheme.provider);
|
|
3655
|
-
registry.add(chainColorTheme.provider);
|
|
3656
2844
|
for (const p of [...plugin.managers.lociLabels.providers]) plugin.managers.lociLabels.removeProvider(p);
|
|
3657
2845
|
plugin.managers.structure.focus.behaviors.current.subscribe((entry) => {
|
|
3658
2846
|
const select = plugin.managers.interactivity.lociSelects;
|
|
@@ -3664,232 +2852,59 @@ function _createViewer() {
|
|
|
3664
2852
|
return _createViewer.apply(this, arguments);
|
|
3665
2853
|
}
|
|
3666
2854
|
/**
|
|
3667
|
-
* The representation parameters Mol*'s own presets compute, read from the same
|
|
3668
|
-
* manager state they read. Ported rather than hardcoded so a chain drawn here
|
|
3669
|
-
* looks exactly like the one the preset used to draw. Mol* hands the same set
|
|
3670
|
-
* to its cartoons and its ball-and-sticks, and so does this.
|
|
3671
|
-
*/
|
|
3672
|
-
function representationTypeParams(plugin) {
|
|
3673
|
-
const { hydrogens, ignoreLight, visualQuality } = plugin.managers.structure.component.state.options;
|
|
3674
|
-
return {
|
|
3675
|
-
ignoreHydrogens: hydrogens !== "all",
|
|
3676
|
-
ignoreHydrogensVariant: hydrogens === "only-polar" ? "non-polar" : "all",
|
|
3677
|
-
ignoreLight,
|
|
3678
|
-
quality: visualQuality
|
|
3679
|
-
};
|
|
3680
|
-
}
|
|
3681
|
-
/**
|
|
3682
|
-
* The two halves of a chain the viewer draws. Each names the component its
|
|
3683
|
-
* half is built under and tags the representation drawn over it, and it is
|
|
3684
|
-
* that tag `applyColorTheme` reads back to tell the two apart.
|
|
3685
|
-
*/
|
|
3686
|
-
const POLYMER_PART = "polymer";
|
|
3687
|
-
const LIGAND_PART = "ligand";
|
|
3688
|
-
/**
|
|
3689
|
-
* True for the representation drawing a chain's heteroatoms.
|
|
3690
|
-
*
|
|
3691
|
-
* Read off the tag put on the representation when it was built, rather than
|
|
3692
|
-
* off the component's key. Mol* does not store the key as it was given: it
|
|
3693
|
-
* files components under `structure-component-<key>`, so a test against the
|
|
3694
|
-
* key as written here silently matches nothing, and every ligand quietly takes
|
|
3695
|
-
* the structure-wide theme instead. The tag survives untouched.
|
|
3696
|
-
*/
|
|
3697
|
-
function isLigandRepresentation(representation) {
|
|
3698
|
-
var _representation$cell$;
|
|
3699
|
-
return ((_representation$cell$ = representation.cell.transform.tags) === null || _representation$cell$ === void 0 ? void 0 : _representation$cell$.includes(LIGAND_PART)) === true;
|
|
3700
|
-
}
|
|
3701
|
-
/**
|
|
3702
|
-
* How heteroatoms are colored: the theme Mol* puts on a ball-and-stick when
|
|
3703
|
-
* left to itself, with the parameters it chooses too.
|
|
3704
|
-
*
|
|
3705
|
-
* Named rather than left blank only so that it survives `applyColorTheme`,
|
|
3706
|
-
* which would otherwise repaint the ligands along with everything else. The
|
|
3707
|
-
* structure-wide theme is the wrong answer for them twice over: a heme reads
|
|
3708
|
-
* as a heme because its iron is orange and its nitrogens are blue, which one
|
|
3709
|
-
* flat color throws away, and pLDDT and residue overlays have no value for a
|
|
3710
|
-
* HETATM to be colored by at all.
|
|
3711
|
-
*/
|
|
3712
|
-
const ELEMENT_THEME = "element-symbol";
|
|
3713
|
-
/**
|
|
3714
|
-
* Draws one chain: its polymer as a cartoon, its ligands and ions as
|
|
3715
|
-
* ball-and-stick.
|
|
3716
|
-
*
|
|
3717
|
-
* Two components rather than one, because a component is what a representation
|
|
3718
|
-
* attaches to and neither representation suits both halves - a cartoon has no
|
|
3719
|
-
* backbone to trace through a heme, and a ball-and-stick over the whole chain
|
|
3720
|
-
* would draw every protein atom as a sphere.
|
|
3721
|
-
*
|
|
3722
|
-
* Hands back the refs of whichever components the chain turned out to have,
|
|
3723
|
-
* which is what `setSubtreeVisibility` is later pointed at. Grouping them
|
|
3724
|
-
* under the chain is what makes hiding a chain take its ligands with it.
|
|
3725
|
-
*/
|
|
3726
|
-
function buildChainComponents(_x5, _x6, _x7, _x8) {
|
|
3727
|
-
return _buildChainComponents.apply(this, arguments);
|
|
3728
|
-
}
|
|
3729
|
-
function _buildChainComponents() {
|
|
3730
|
-
_buildChainComponents = _asyncToGenerator(function* (plugin, structure, chain, typeParams) {
|
|
3731
|
-
const builders = plugin.builders.structure;
|
|
3732
|
-
const refs = [];
|
|
3733
|
-
const addPart = function() {
|
|
3734
|
-
var _ref = _asyncToGenerator(function* (part, expression, props) {
|
|
3735
|
-
const component = yield builders.tryCreateComponentFromExpression(structure, expression, `${part}-${chain.chainId}`, { label: chain.label });
|
|
3736
|
-
if (!component) return;
|
|
3737
|
-
yield builders.representation.addRepresentation(component, props, { tag: part });
|
|
3738
|
-
refs.push(component.ref);
|
|
3739
|
-
});
|
|
3740
|
-
return function addPart(_x2, _x3, _x4) {
|
|
3741
|
-
return _ref.apply(this, arguments);
|
|
3742
|
-
};
|
|
3743
|
-
}();
|
|
3744
|
-
yield addPart(POLYMER_PART, chainPolymerExpression(chain.chainId), {
|
|
3745
|
-
type: "cartoon",
|
|
3746
|
-
typeParams
|
|
3747
|
-
});
|
|
3748
|
-
yield addPart(LIGAND_PART, chainLigandExpression(chain.chainId), {
|
|
3749
|
-
color: ELEMENT_THEME,
|
|
3750
|
-
type: "ball-and-stick",
|
|
3751
|
-
typeParams
|
|
3752
|
-
});
|
|
3753
|
-
return refs;
|
|
3754
|
-
});
|
|
3755
|
-
return _buildChainComponents.apply(this, arguments);
|
|
3756
|
-
}
|
|
3757
|
-
const NOTHING_LOADED = {
|
|
3758
|
-
chains: [],
|
|
3759
|
-
componentRefs: /* @__PURE__ */ new Map(),
|
|
3760
|
-
residuesByChain: /* @__PURE__ */ new Map()
|
|
3761
|
-
};
|
|
3762
|
-
/**
|
|
3763
|
-
* Parses the structure and draws each chain: a cartoon over its polymer, and
|
|
3764
|
-
* ball-and-stick over its ligands and ions.
|
|
3765
|
-
*
|
|
3766
|
-
* Mol*'s `default` hierarchy preset would be shorter, but it groups every
|
|
3767
|
-
* polymer chain into a single component, and a component is the unit Mol* can
|
|
3768
|
-
* hide. Building them per chain is what makes a chain individually hideable;
|
|
3769
|
-
* the refs handed back are what `setSubtreeVisibility` is later pointed at.
|
|
3770
|
-
*
|
|
3771
|
-
* The wrapper detects PDB vs mmCIF from the text; this just forwards that
|
|
3772
|
-
* guess to Mol*.
|
|
3773
|
-
*/
|
|
3774
|
-
function loadStructure(_x9, _x10, _x11, _x12) {
|
|
3775
|
-
return _loadStructure.apply(this, arguments);
|
|
3776
|
-
}
|
|
3777
|
-
function _loadStructure() {
|
|
3778
|
-
_loadStructure = _asyncToGenerator(function* (plugin, structureText, usePlddtColoring, showAxes) {
|
|
3779
|
-
try {
|
|
3780
|
-
var _plugin$canvas3d;
|
|
3781
|
-
yield plugin.clear();
|
|
3782
|
-
const data = yield plugin.builders.data.rawData({
|
|
3783
|
-
data: structureText,
|
|
3784
|
-
label: "Structure"
|
|
3785
|
-
});
|
|
3786
|
-
const trajectory = yield plugin.builders.structure.parseTrajectory(data, detectStructureFormat(structureText));
|
|
3787
|
-
const model = yield plugin.builders.structure.createModel(trajectory);
|
|
3788
|
-
const structure = yield plugin.builders.structure.createStructure(model, {
|
|
3789
|
-
name: "model",
|
|
3790
|
-
params: {}
|
|
3791
|
-
});
|
|
3792
|
-
const data3d = structure.data;
|
|
3793
|
-
if (!data3d) return NOTHING_LOADED;
|
|
3794
|
-
const { chainLabels, chains, residuesByChain } = scanChains(data3d);
|
|
3795
|
-
const typeParams = representationTypeParams(plugin);
|
|
3796
|
-
const componentRefs = /* @__PURE__ */ new Map();
|
|
3797
|
-
for (const [chainId, label] of chainLabels) {
|
|
3798
|
-
const refs = yield buildChainComponents(plugin, structure, {
|
|
3799
|
-
chainId,
|
|
3800
|
-
label
|
|
3801
|
-
}, typeParams);
|
|
3802
|
-
if (refs.length > 0) componentRefs.set(chainId, refs);
|
|
3803
|
-
}
|
|
3804
|
-
if (usePlddtColoring) yield applyColorTheme(plugin, PLDDT_THEME_NAME);
|
|
3805
|
-
(_plugin$canvas3d = plugin.canvas3d) === null || _plugin$canvas3d === void 0 || _plugin$canvas3d.requestCameraReset();
|
|
3806
|
-
setAxes(plugin, showAxes);
|
|
3807
|
-
return {
|
|
3808
|
-
chains,
|
|
3809
|
-
componentRefs,
|
|
3810
|
-
residuesByChain
|
|
3811
|
-
};
|
|
3812
|
-
} catch (error) {
|
|
3813
|
-
console.error("Failed to load structure:", error);
|
|
3814
|
-
return NOTHING_LOADED;
|
|
3815
|
-
}
|
|
3816
|
-
});
|
|
3817
|
-
return _loadStructure.apply(this, arguments);
|
|
3818
|
-
}
|
|
3819
|
-
/**
|
|
3820
|
-
* Shows or hides everything each chain is drawn with, cartoon and sticks
|
|
3821
|
-
* alike. Every component under the chain, so hiding it takes its ligands with
|
|
3822
|
-
* it rather than leaving a heme floating where its protein used to be.
|
|
3823
|
-
*
|
|
3824
|
-
* The whole subtree of each, not just the component cell: visibility is
|
|
3825
|
-
* per-cell, and it is the representation underneath that actually draws.
|
|
3826
|
-
*
|
|
3827
|
-
* Set outright rather than through the component manager's `toggleVisibility`,
|
|
3828
|
-
* which flips whatever it finds: the hidden set is derived from props on every
|
|
3829
|
-
* pass, so it has to be applied as a statement of what should be true and not
|
|
3830
|
-
* as a change to what already is.
|
|
3831
|
-
*/
|
|
3832
|
-
function applyChainVisibility(plugin, componentRefs, hidden) {
|
|
3833
|
-
for (const [chainId, refs] of componentRefs) for (const ref of refs) (0, molstar_lib_mol_plugin_behavior_static_state.setSubtreeVisibility)(plugin.state.data, ref, hidden.has(chainId));
|
|
3834
|
-
}
|
|
3835
|
-
/**
|
|
3836
|
-
* Recolors every loaded structure's representations with the named theme,
|
|
3837
|
-
* leaving the heteroatoms on `ELEMENT_THEME`.
|
|
3838
|
-
*
|
|
3839
|
-
* The per-representation form of `updateRepresentationsTheme`, so the two can
|
|
3840
|
-
* be told apart in one pass; handing it a filtered list instead would leave
|
|
3841
|
-
* the ligands on whichever theme happened to be current when they were built.
|
|
3842
|
-
*/
|
|
3843
|
-
function applyColorTheme(_x13, _x14) {
|
|
3844
|
-
return _applyColorTheme.apply(this, arguments);
|
|
3845
|
-
}
|
|
3846
|
-
function _applyColorTheme() {
|
|
3847
|
-
_applyColorTheme = _asyncToGenerator(function* (plugin, colorTheme) {
|
|
3848
|
-
yield plugin.dataTransaction(_asyncToGenerator(function* () {
|
|
3849
|
-
for (const s of plugin.managers.structure.hierarchy.current.structures) yield plugin.managers.structure.component.updateRepresentationsTheme(s.components, (_component, representation) => ({ color: isLigandRepresentation(representation) ? ELEMENT_THEME : colorTheme }));
|
|
3850
|
-
}));
|
|
3851
|
-
});
|
|
3852
|
-
return _applyColorTheme.apply(this, arguments);
|
|
3853
|
-
}
|
|
3854
|
-
/**
|
|
3855
2855
|
* Owns the Mol* plugin for one viewer: creates it, loads the structure, wires
|
|
3856
2856
|
* interaction callbacks, and disposes of it on unmount.
|
|
3857
2857
|
*
|
|
3858
2858
|
* The plugin is built once and then mutated in place. Creating it is expensive
|
|
3859
2859
|
* and destroys the camera, so prop changes that Mol* can absorb (background,
|
|
3860
|
-
* axes, coloring, new structure) are pushed in through the
|
|
3861
|
-
* the sibling hooks rather than by rebuilding.
|
|
2860
|
+
* axes, coloring, representation, new structure) are pushed in through the
|
|
2861
|
+
* effects below and in the sibling hooks rather than by rebuilding.
|
|
3862
2862
|
*/
|
|
3863
|
-
function useMolstarPlugin({ backgroundColor,
|
|
2863
|
+
function useMolstarPlugin({ backgroundColor, containerRef, download, edgeColor, hiddenChains, highlightColor, mode, molstarSpec, onChainHover, onChainSelect, onChainToggle, onDispose, onError, onReady, onResidueClick, onResidueHover, onSelectionChange, onSelectionClear, onStructureLoad, sceneMode, sceneProps, selectedChains, sequenceViewerBackgroundColor, showAxes, showSequenceViewer, structure }) {
|
|
2864
|
+
var _sceneProps$chainColo;
|
|
3864
2865
|
const pluginRef = (0, react.useRef)(null);
|
|
3865
|
-
const
|
|
3866
|
-
const chainColorThemeRef = (0, react.useRef)(null);
|
|
2866
|
+
const sceneRef = (0, react.useRef)(null);
|
|
3867
2867
|
const currentStructureRef = (0, react.useRef)(null);
|
|
3868
2868
|
const [isReady, setIsReady] = (0, react.useState)(false);
|
|
3869
2869
|
const [chains, setChains] = (0, react.useState)([]);
|
|
3870
2870
|
const [loadCount, setLoadCount] = (0, react.useState)(0);
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
* visibility effect points `setSubtreeVisibility` at. Rewritten by every
|
|
3874
|
-
* load, since the refs do not survive `plugin.clear()`.
|
|
3875
|
-
*/
|
|
3876
|
-
const componentRefsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
2871
|
+
const [sceneVersion, setSceneVersion] = (0, react.useState)(0);
|
|
2872
|
+
const [sceneBusy, setSceneBusy] = (0, react.useState)(false);
|
|
3877
2873
|
/**
|
|
3878
2874
|
* Which residues sit on each chain, for the readout to average a score over
|
|
3879
2875
|
* a whole-chain selection. A ref rather than state: nothing renders from it
|
|
3880
2876
|
* directly, and it is rewritten in step with `chains`, which does.
|
|
3881
2877
|
*/
|
|
3882
2878
|
const residuesByChainRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
2879
|
+
const addressIndexRef = (0, react.useRef)(/* @__PURE__ */ new Map());
|
|
2880
|
+
const framedOrientationRef = (0, react.useRef)(void 0);
|
|
3883
2881
|
/**
|
|
3884
|
-
* The
|
|
3885
|
-
*
|
|
3886
|
-
*
|
|
3887
|
-
* them - a new Set each render would reload the structure on every pass.
|
|
2882
|
+
* The scene as the props describe it, read by a load when it comes to draw.
|
|
2883
|
+
* Through a ref because a load outlasts renders and has to draw what the
|
|
2884
|
+
* props say when it gets there, not what they said when it began.
|
|
3888
2885
|
*/
|
|
3889
|
-
const
|
|
3890
|
-
|
|
3891
|
-
const
|
|
3892
|
-
|
|
2886
|
+
const scenePropsRef = (0, react.useRef)(sceneProps);
|
|
2887
|
+
scenePropsRef.current = sceneProps;
|
|
2888
|
+
const initialPropsRef = (0, react.useRef)({
|
|
2889
|
+
backgroundColor,
|
|
2890
|
+
edgeColor,
|
|
2891
|
+
highlightColor,
|
|
2892
|
+
mode,
|
|
2893
|
+
molstarSpec,
|
|
2894
|
+
showAxes,
|
|
2895
|
+
showSequenceViewer,
|
|
2896
|
+
structure
|
|
2897
|
+
});
|
|
2898
|
+
initialPropsRef.current = {
|
|
2899
|
+
backgroundColor,
|
|
2900
|
+
edgeColor,
|
|
2901
|
+
highlightColor,
|
|
2902
|
+
mode,
|
|
2903
|
+
molstarSpec,
|
|
2904
|
+
showAxes,
|
|
2905
|
+
showSequenceViewer,
|
|
2906
|
+
structure
|
|
2907
|
+
};
|
|
3893
2908
|
/**
|
|
3894
2909
|
* Carries the theme-dependent props into the views Mol* renders in its own
|
|
3895
2910
|
* React root. They cannot read them from context, and rebuilding the plugin
|
|
@@ -3911,19 +2926,17 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
3911
2926
|
clipRatioRef.current = ratio;
|
|
3912
2927
|
}, []);
|
|
3913
2928
|
/**
|
|
3914
|
-
* Takes up what a load produced:
|
|
3915
|
-
*
|
|
3916
|
-
* the consumer is told about.
|
|
2929
|
+
* Takes up what a load produced: what the chain-keyed props and addresses
|
|
2930
|
+
* are resolved against, and the chain list the consumer is told about.
|
|
3917
2931
|
*
|
|
3918
2932
|
* Chains are published only when they actually differ, so reloading the same
|
|
3919
|
-
* structure - a
|
|
3920
|
-
*
|
|
2933
|
+
* structure - a parent handing back an equal PDB string - does not hand every
|
|
2934
|
+
* consumer a new array to react to.
|
|
3921
2935
|
*/
|
|
3922
|
-
const adoptLoadedStructure = (0, react.useCallback)((
|
|
3923
|
-
componentRefsRef.current = loaded.componentRefs;
|
|
2936
|
+
const adoptLoadedStructure = (0, react.useCallback)((loaded) => {
|
|
3924
2937
|
residuesByChainRef.current = loaded.residuesByChain;
|
|
3925
|
-
|
|
3926
|
-
setChains((prev) => chainsEqual(prev, loaded.chains) ? prev : loaded.chains);
|
|
2938
|
+
addressIndexRef.current = loaded.addressIndex;
|
|
2939
|
+
setChains((prev) => require_renderStructureImage.chainsEqual(prev, loaded.chains) ? prev : loaded.chains);
|
|
3927
2940
|
setLoadCount((count) => count + 1);
|
|
3928
2941
|
}, []);
|
|
3929
2942
|
const onResidueClickRef = (0, react.useRef)(onResidueClick);
|
|
@@ -3934,30 +2947,113 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
3934
2947
|
onSelectionChangeRef.current = onSelectionChange;
|
|
3935
2948
|
const onResidueHoverRef = (0, react.useRef)(onResidueHover);
|
|
3936
2949
|
onResidueHoverRef.current = onResidueHover;
|
|
2950
|
+
const onReadyRef = (0, react.useRef)(onReady);
|
|
2951
|
+
onReadyRef.current = onReady;
|
|
2952
|
+
const onErrorRef = (0, react.useRef)(onError);
|
|
2953
|
+
onErrorRef.current = onError;
|
|
2954
|
+
const onDisposeRef = (0, react.useRef)(onDispose);
|
|
2955
|
+
onDisposeRef.current = onDispose;
|
|
2956
|
+
const onStructureLoadRef = (0, react.useRef)(onStructureLoad);
|
|
2957
|
+
onStructureLoadRef.current = onStructureLoad;
|
|
2958
|
+
/**
|
|
2959
|
+
* Fixed at the first render: the load path draws or declines to draw by it,
|
|
2960
|
+
* so a mode that changed under a live plugin would leave its scene half one
|
|
2961
|
+
* and half the other.
|
|
2962
|
+
*/
|
|
2963
|
+
const sceneModeRef = (0, react.useRef)(sceneMode);
|
|
2964
|
+
/**
|
|
2965
|
+
* The plugin last handed to `onReady`, so that `onDispose` is only called for
|
|
2966
|
+
* one a consumer has actually been given - and is called for it even while
|
|
2967
|
+
* its `onReady` is still running.
|
|
2968
|
+
*/
|
|
2969
|
+
const handedOverRef = (0, react.useRef)(null);
|
|
3937
2970
|
/** Last residue reported to `onResidueHover`, to suppress repeats. */
|
|
3938
2971
|
const lastHoverRef = (0, react.useRef)(null);
|
|
3939
|
-
const
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
2972
|
+
const reportError = (0, react.useCallback)((error, phase) => {
|
|
2973
|
+
const handler = onErrorRef.current;
|
|
2974
|
+
if (handler) handler(error, phase);
|
|
2975
|
+
else console.error(UNHANDLED_ERROR_MESSAGES[phase], error);
|
|
2976
|
+
}, []);
|
|
2977
|
+
/**
|
|
2978
|
+
* Hands a loaded structure to `onReady` and waits for it.
|
|
2979
|
+
*
|
|
2980
|
+
* `isCurrent` says whether the plugin is still the viewer's own by the time
|
|
2981
|
+
* the consumer is done with it. A failure after that is not reported: the
|
|
2982
|
+
* plugin it happened on is gone, and the consumer has been told so through
|
|
2983
|
+
* `onDispose` already.
|
|
2984
|
+
*/
|
|
2985
|
+
const handOver = (0, react.useCallback)(function() {
|
|
2986
|
+
var _ref = require_renderStructureImage._asyncToGenerator(function* (plugin, loaded, isCurrent) {
|
|
2987
|
+
const ready = onReadyRef.current;
|
|
2988
|
+
const { atomCount, chains: loadedChains, structure: parsed } = loaded;
|
|
2989
|
+
if (!ready || !parsed) return;
|
|
2990
|
+
handedOverRef.current = plugin;
|
|
2991
|
+
try {
|
|
2992
|
+
yield ready(plugin, {
|
|
2993
|
+
atomCount,
|
|
2994
|
+
chains: loadedChains,
|
|
2995
|
+
structure: parsed
|
|
2996
|
+
});
|
|
2997
|
+
} catch (error) {
|
|
2998
|
+
if (isCurrent()) reportError(error, "load");
|
|
2999
|
+
}
|
|
3000
|
+
});
|
|
3001
|
+
return function(_x2, _x3, _x4) {
|
|
3002
|
+
return _ref.apply(this, arguments);
|
|
3003
|
+
};
|
|
3004
|
+
}(), [reportError]);
|
|
3005
|
+
/**
|
|
3006
|
+
* Takes up whatever a load produced. A failed load leaves the viewer holding
|
|
3007
|
+
* nothing, as a structure with no chains would, and is reported rather than
|
|
3008
|
+
* handed to `onReady`.
|
|
3009
|
+
*/
|
|
3010
|
+
const takeUpLoad = (0, react.useCallback)(function() {
|
|
3011
|
+
var _ref2 = require_renderStructureImage._asyncToGenerator(function* (plugin, outcome, isCurrent) {
|
|
3012
|
+
var _onStructureLoadRef$c;
|
|
3013
|
+
if (!isCurrent()) return;
|
|
3014
|
+
if (!outcome.ok) {
|
|
3015
|
+
adoptLoadedStructure(require_renderStructureImage.NOTHING_LOADED);
|
|
3016
|
+
reportError(outcome.error, "load");
|
|
3017
|
+
return;
|
|
3018
|
+
}
|
|
3019
|
+
const { loaded } = outcome;
|
|
3020
|
+
adoptLoadedStructure(loaded);
|
|
3021
|
+
(_onStructureLoadRef$c = onStructureLoadRef.current) === null || _onStructureLoadRef$c === void 0 || _onStructureLoadRef$c.call(onStructureLoadRef, require_renderStructureImage.loadInfo(loaded));
|
|
3022
|
+
yield handOver(plugin, loaded, isCurrent);
|
|
3023
|
+
});
|
|
3024
|
+
return function(_x5, _x6, _x7) {
|
|
3025
|
+
return _ref2.apply(this, arguments);
|
|
3026
|
+
};
|
|
3027
|
+
}(), [
|
|
3028
|
+
adoptLoadedStructure,
|
|
3029
|
+
handOver,
|
|
3030
|
+
reportError
|
|
3031
|
+
]);
|
|
3032
|
+
/**
|
|
3033
|
+
* Loads a structure and brings everything up on it: parses it, draws the
|
|
3034
|
+
* scene when the viewer is the one drawing it, places the camera, and hands
|
|
3035
|
+
* it over. Runs inside the scene's queue, so it never interleaves with an
|
|
3036
|
+
* update to the structure it is replacing.
|
|
3037
|
+
*
|
|
3038
|
+
* The camera is placed once something is drawn: Mol* settles a reset on the
|
|
3039
|
+
* next frame, and one settled against an empty scene is lost. So it follows
|
|
3040
|
+
* the viewer's own scene when there is one, and the consumer's `onReady`
|
|
3041
|
+
* when the consumer draws.
|
|
3042
|
+
*/
|
|
3043
|
+
const loadAndDraw = (0, react.useCallback)(function() {
|
|
3044
|
+
var _ref3 = require_renderStructureImage._asyncToGenerator(function* (plugin, scene, text, isCurrent) {
|
|
3045
|
+
const outcome = yield scene.load(text, scenePropsRef.current);
|
|
3046
|
+
if (!isCurrent()) return;
|
|
3047
|
+
const managed = sceneModeRef.current === "managed";
|
|
3048
|
+
if (outcome.ok) setAxes(plugin, initialPropsRef.current.showAxes);
|
|
3049
|
+
if (managed && outcome.ok) framedOrientationRef.current = scene.frame(scenePropsRef.current, true);
|
|
3050
|
+
yield takeUpLoad(plugin, outcome, isCurrent);
|
|
3051
|
+
if (!managed && outcome.ok && isCurrent()) framedOrientationRef.current = scene.frame(scenePropsRef.current, false);
|
|
3052
|
+
});
|
|
3053
|
+
return function(_x8, _x9, _x10, _x11) {
|
|
3054
|
+
return _ref3.apply(this, arguments);
|
|
3055
|
+
};
|
|
3056
|
+
}(), [takeUpLoad]);
|
|
3961
3057
|
/**
|
|
3962
3058
|
* The consumer's `canvas3d` overrides, keyed by content. Unlike the rest of
|
|
3963
3059
|
* the spec these are re-applied when they change, and they commonly arrive
|
|
@@ -3978,7 +3074,7 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
3978
3074
|
let cancelled = false;
|
|
3979
3075
|
let clipSubscription;
|
|
3980
3076
|
const init = function() {
|
|
3981
|
-
var
|
|
3077
|
+
var _ref4 = require_renderStructureImage._asyncToGenerator(function* () {
|
|
3982
3078
|
yield nextFrame();
|
|
3983
3079
|
if (cancelled) return;
|
|
3984
3080
|
const rect = container.getBoundingClientRect();
|
|
@@ -3988,16 +3084,12 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
3988
3084
|
}
|
|
3989
3085
|
const initial = initialPropsRef.current;
|
|
3990
3086
|
try {
|
|
3991
|
-
var _plugin$
|
|
3992
|
-
const residueValueTheme = createResidueValueTheme(initial.mode);
|
|
3993
|
-
const chainColorTheme = createChainColorTheme();
|
|
3087
|
+
var _plugin$canvas3d;
|
|
3994
3088
|
const plugin = yield createViewer({
|
|
3995
3089
|
backgroundColor: initial.backgroundColor,
|
|
3996
|
-
chainColorTheme,
|
|
3997
3090
|
edgeColor: initial.edgeColor,
|
|
3998
3091
|
highlightColor: initial.highlightColor,
|
|
3999
3092
|
molstarSpec: initial.molstarSpec,
|
|
4000
|
-
residueValueTheme,
|
|
4001
3093
|
root: container,
|
|
4002
3094
|
showAxes: initial.showAxes,
|
|
4003
3095
|
showSequenceViewer: initial.showSequenceViewer,
|
|
@@ -4008,12 +3100,18 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4008
3100
|
return;
|
|
4009
3101
|
}
|
|
4010
3102
|
pluginRef.current = plugin;
|
|
4011
|
-
|
|
4012
|
-
|
|
3103
|
+
const scene = new require_renderStructureImage.StructureScene(plugin, {
|
|
3104
|
+
draw: sceneModeRef.current === "managed",
|
|
3105
|
+
mode: initial.mode,
|
|
3106
|
+
onBusyChange: setSceneBusy,
|
|
3107
|
+
onRebuild: () => setSceneVersion((version) => version + 1),
|
|
3108
|
+
reportError
|
|
3109
|
+
});
|
|
3110
|
+
sceneRef.current = scene;
|
|
4013
3111
|
const latest = initialPropsRef.current;
|
|
4014
|
-
const loaded = yield loadStructure(plugin, latest.structure, latest.hasPlddt, latest.showAxes);
|
|
4015
3112
|
currentStructureRef.current = latest.structure;
|
|
4016
|
-
|
|
3113
|
+
yield scene.enqueue(() => loadAndDraw(plugin, scene, latest.structure, () => !cancelled));
|
|
3114
|
+
if (cancelled) return;
|
|
4017
3115
|
/**
|
|
4018
3116
|
* Mol*'s click behavior is a BehaviorSubject, so subscribing replays
|
|
4019
3117
|
* whatever it is currently holding - which, before anything has been
|
|
@@ -4051,37 +3149,46 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4051
3149
|
lastHoverRef.current = residue;
|
|
4052
3150
|
(_onResidueHoverRef$cu = onResidueHoverRef.current) === null || _onResidueHoverRef$cu === void 0 || _onResidueHoverRef$cu.call(onResidueHoverRef, residue);
|
|
4053
3151
|
});
|
|
4054
|
-
clipSubscription = (_plugin$
|
|
3152
|
+
clipSubscription = (_plugin$canvas3d = plugin.canvas3d) === null || _plugin$canvas3d === void 0 ? void 0 : _plugin$canvas3d.didDraw.subscribe(() => {
|
|
4055
3153
|
syncClipToZoom(plugin, () => clipRatioRef.current);
|
|
4056
3154
|
});
|
|
4057
3155
|
if (!cancelled) setIsReady(true);
|
|
4058
3156
|
} catch (error) {
|
|
4059
|
-
|
|
3157
|
+
if (!cancelled) reportError(error, "init");
|
|
4060
3158
|
}
|
|
4061
3159
|
});
|
|
4062
3160
|
return function init() {
|
|
4063
|
-
return
|
|
3161
|
+
return _ref4.apply(this, arguments);
|
|
4064
3162
|
};
|
|
4065
3163
|
}();
|
|
4066
3164
|
init();
|
|
4067
3165
|
return () => {
|
|
4068
|
-
var
|
|
3166
|
+
var _sceneRef$current;
|
|
4069
3167
|
cancelled = true;
|
|
4070
3168
|
clipSubscription === null || clipSubscription === void 0 || clipSubscription.unsubscribe();
|
|
4071
|
-
(
|
|
3169
|
+
(_sceneRef$current = sceneRef.current) === null || _sceneRef$current === void 0 || _sceneRef$current.dispose();
|
|
3170
|
+
sceneRef.current = null;
|
|
3171
|
+
const plugin = pluginRef.current;
|
|
3172
|
+
try {
|
|
3173
|
+
if (plugin && handedOverRef.current === plugin) {
|
|
3174
|
+
var _onDisposeRef$current;
|
|
3175
|
+
handedOverRef.current = null;
|
|
3176
|
+
(_onDisposeRef$current = onDisposeRef.current) === null || _onDisposeRef$current === void 0 || _onDisposeRef$current.call(onDisposeRef);
|
|
3177
|
+
}
|
|
3178
|
+
} finally {
|
|
3179
|
+
plugin === null || plugin === void 0 || plugin.dispose();
|
|
3180
|
+
}
|
|
4072
3181
|
pluginRef.current = null;
|
|
4073
|
-
residueValueThemeRef.current = null;
|
|
4074
|
-
chainColorThemeRef.current = null;
|
|
4075
|
-
componentRefsRef.current = /* @__PURE__ */ new Map();
|
|
4076
3182
|
residuesByChainRef.current = /* @__PURE__ */ new Map();
|
|
3183
|
+
addressIndexRef.current = /* @__PURE__ */ new Map();
|
|
4077
3184
|
currentStructureRef.current = null;
|
|
4078
3185
|
clipRatioRef.current = null;
|
|
4079
3186
|
setIsReady(false);
|
|
4080
3187
|
};
|
|
4081
3188
|
}, []);
|
|
4082
3189
|
(0, react.useEffect)(() => {
|
|
4083
|
-
var _pluginRef$
|
|
4084
|
-
const canvas3d = (_pluginRef$
|
|
3190
|
+
var _pluginRef$current, _initialPropsRef$curr;
|
|
3191
|
+
const canvas3d = (_pluginRef$current = pluginRef.current) === null || _pluginRef$current === void 0 ? void 0 : _pluginRef$current.canvas3d;
|
|
4085
3192
|
if (!canvas3d || !isReady) return;
|
|
4086
3193
|
canvas3d.setProps({
|
|
4087
3194
|
marking: {
|
|
@@ -4110,6 +3217,8 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4110
3217
|
onChainHover,
|
|
4111
3218
|
onChainSelect,
|
|
4112
3219
|
onChainToggle,
|
|
3220
|
+
onError: reportError,
|
|
3221
|
+
sceneBusy,
|
|
4113
3222
|
selectedChains,
|
|
4114
3223
|
sequenceViewerBackgroundColor,
|
|
4115
3224
|
showAxes
|
|
@@ -4121,6 +3230,8 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4121
3230
|
onChainHover,
|
|
4122
3231
|
onChainSelect,
|
|
4123
3232
|
onChainToggle,
|
|
3233
|
+
reportError,
|
|
3234
|
+
sceneBusy,
|
|
4124
3235
|
selectedChains,
|
|
4125
3236
|
sequenceViewerBackgroundColor,
|
|
4126
3237
|
showAxes,
|
|
@@ -4132,38 +3243,88 @@ function useMolstarPlugin({ backgroundColor, chainColors, containerRef, download
|
|
|
4132
3243
|
}, [showAxes, isReady]);
|
|
4133
3244
|
(0, react.useEffect)(() => {
|
|
4134
3245
|
const plugin = pluginRef.current;
|
|
4135
|
-
|
|
3246
|
+
const scene = sceneRef.current;
|
|
3247
|
+
if (!plugin || !scene || !isReady) return;
|
|
4136
3248
|
if (structure === currentStructureRef.current) return;
|
|
4137
3249
|
currentStructureRef.current = structure;
|
|
4138
3250
|
clipRatioRef.current = null;
|
|
4139
3251
|
lastHoverRef.current = null;
|
|
4140
|
-
|
|
4141
|
-
if (pluginRef.current
|
|
4142
|
-
adoptLoadedStructure(plugin, loaded);
|
|
3252
|
+
scene.enqueue(() => loadAndDraw(plugin, scene, structure, () => pluginRef.current === plugin)).catch((error) => {
|
|
3253
|
+
if (pluginRef.current === plugin) reportError(error, "load");
|
|
4143
3254
|
});
|
|
4144
3255
|
}, [
|
|
4145
3256
|
structure,
|
|
4146
|
-
hasPlddt,
|
|
4147
3257
|
isReady,
|
|
4148
|
-
|
|
3258
|
+
loadAndDraw,
|
|
3259
|
+
reportError
|
|
4149
3260
|
]);
|
|
3261
|
+
/**
|
|
3262
|
+
* Keys for the scene props that arrive as fresh objects on every render, so
|
|
3263
|
+
* the update below turns on what they say rather than on their identity.
|
|
3264
|
+
* `plddt` and the overlay are compared by identity, as they are elsewhere.
|
|
3265
|
+
*/
|
|
3266
|
+
const hiddenKey = require_renderStructureImage.chainSetKey(sceneProps.hiddenChains);
|
|
3267
|
+
const sceneHighlightsKey = require_renderStructureImage.highlightsKey(sceneProps.highlights);
|
|
3268
|
+
const chainColorsKey = JSON.stringify((_sceneProps$chainColo = sceneProps.chainColors) !== null && _sceneProps$chainColo !== void 0 ? _sceneProps$chainColo : null);
|
|
4150
3269
|
(0, react.useEffect)(() => {
|
|
4151
|
-
const
|
|
4152
|
-
if (!
|
|
4153
|
-
|
|
4154
|
-
}, [
|
|
3270
|
+
const scene = sceneRef.current;
|
|
3271
|
+
if (!scene || !isReady || sceneModeRef.current !== "managed") return;
|
|
3272
|
+
scene.update(scenePropsRef.current);
|
|
3273
|
+
}, [
|
|
3274
|
+
isReady,
|
|
3275
|
+
sceneProps.representation,
|
|
3276
|
+
hiddenKey,
|
|
3277
|
+
sceneHighlightsKey,
|
|
3278
|
+
sceneProps.colorBy,
|
|
3279
|
+
chainColorsKey,
|
|
3280
|
+
sceneProps.plddt,
|
|
3281
|
+
sceneProps.overlay,
|
|
3282
|
+
sceneProps.mode
|
|
3283
|
+
]);
|
|
4155
3284
|
return {
|
|
4156
|
-
|
|
3285
|
+
addressIndexRef,
|
|
4157
3286
|
chains,
|
|
3287
|
+
framedOrientationRef,
|
|
4158
3288
|
isReady,
|
|
4159
3289
|
loadCount,
|
|
4160
3290
|
pluginRef,
|
|
4161
3291
|
residuesByChainRef,
|
|
4162
|
-
|
|
3292
|
+
sceneMode: sceneModeRef.current,
|
|
3293
|
+
sceneRef,
|
|
3294
|
+
sceneVersion,
|
|
4163
3295
|
setClipRatio
|
|
4164
3296
|
};
|
|
4165
3297
|
}
|
|
4166
3298
|
//#endregion
|
|
3299
|
+
//#region src/core/ProteinStructureViewer/hooks/useSelection.ts
|
|
3300
|
+
/**
|
|
3301
|
+
* Reconciles the selection the consumer passes with the one the viewer keeps.
|
|
3302
|
+
*
|
|
3303
|
+
* Selection is controlled when `selection` is passed and owned here when it is
|
|
3304
|
+
* not, the same split `useChains` makes for visibility. The camera only ever
|
|
3305
|
+
* follows the selection, so without an owner a click on a viewer nobody holds
|
|
3306
|
+
* state for would mark the residue and never zoom in on it.
|
|
3307
|
+
* `onSelectionChange` fires either way: a consumer that only wants to follow
|
|
3308
|
+
* along should not have to take ownership to hear about it.
|
|
3309
|
+
*/
|
|
3310
|
+
function useSelection({ onSelectionChange, selection, structure }) {
|
|
3311
|
+
const isControlled = selection !== void 0;
|
|
3312
|
+
const [ownSelection, setOwnSelection] = (0, react.useState)(null);
|
|
3313
|
+
const onSelectionChangeRef = (0, react.useRef)(onSelectionChange);
|
|
3314
|
+
onSelectionChangeRef.current = onSelectionChange;
|
|
3315
|
+
(0, react.useEffect)(() => {
|
|
3316
|
+
setOwnSelection(null);
|
|
3317
|
+
}, [structure]);
|
|
3318
|
+
return {
|
|
3319
|
+
changeSelection: (0, react.useCallback)((next) => {
|
|
3320
|
+
var _onSelectionChangeRef;
|
|
3321
|
+
if (!isControlled) setOwnSelection(next);
|
|
3322
|
+
(_onSelectionChangeRef = onSelectionChangeRef.current) === null || _onSelectionChangeRef === void 0 || _onSelectionChangeRef.call(onSelectionChangeRef, next);
|
|
3323
|
+
}, [isControlled]),
|
|
3324
|
+
selection: isControlled ? selection : ownSelection
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
//#endregion
|
|
4167
3328
|
//#region src/core/ProteinStructureViewer/utils/focusRepresentation.ts
|
|
4168
3329
|
/**
|
|
4169
3330
|
* What Mol* draws around a focused selection, as ball-and-stick over the
|
|
@@ -4203,7 +3364,7 @@ function setFocusNeighbourhood(_x, _x2) {
|
|
|
4203
3364
|
return _setFocusNeighbourhood.apply(this, arguments);
|
|
4204
3365
|
}
|
|
4205
3366
|
function _setFocusNeighbourhood() {
|
|
4206
|
-
_setFocusNeighbourhood = _asyncToGenerator(function* (plugin, includeNeighbours) {
|
|
3367
|
+
_setFocusNeighbourhood = require_renderStructureImage._asyncToGenerator(function* (plugin, includeNeighbours) {
|
|
4207
3368
|
const expandRadius = includeNeighbours ? NEIGHBOUR_RADIUS : OWN_RESIDUES_ONLY;
|
|
4208
3369
|
yield plugin.state.updateBehavior(molstar_lib_mol_plugin_behavior_dynamic_selection_structure_focus_representation.StructureFocusRepresentation, (params) => {
|
|
4209
3370
|
params.components = FOCUS_COMPONENTS;
|
|
@@ -4282,10 +3443,11 @@ function focusWithShell(plugin, loci, includeNeighbours, currentShell, pluginRef
|
|
|
4282
3443
|
* chains around them instead, in `useChainHighlight`.
|
|
4283
3444
|
*
|
|
4284
3445
|
* The click subscription only reports the selection it was given, so a click
|
|
4285
|
-
* moves the camera only once the
|
|
4286
|
-
*
|
|
4287
|
-
* marks immediately, through Mol*'s own focus
|
|
4288
|
-
* responds to a click
|
|
3446
|
+
* moves the camera only once whoever owns the selection accepts it: the
|
|
3447
|
+
* consumer echoing it back when `selection` is controlled, the viewer itself
|
|
3448
|
+
* when it is not. Clicking still marks immediately, through Mol*'s own focus
|
|
3449
|
+
* behavior, so the structure responds to a click even when a consumer
|
|
3450
|
+
* controlling the selection declines it.
|
|
4289
3451
|
*/
|
|
4290
3452
|
function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setClipRatio }) {
|
|
4291
3453
|
var _selection$chains, _selection$residues;
|
|
@@ -4312,7 +3474,7 @@ function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setCli
|
|
|
4312
3474
|
* itself - and comparing strings rather than objects is what keeps a consumer
|
|
4313
3475
|
* echoing an equal selection back from refocusing on every render.
|
|
4314
3476
|
*/
|
|
4315
|
-
const focusKey = selection ? `${selectionKey(selection)}#${[...hiddenChains].sort().join(",")}` : null;
|
|
3477
|
+
const focusKey = selection ? `${require_renderStructureImage.selectionKey(selection)}#${[...hiddenChains].sort().join(",")}` : null;
|
|
4316
3478
|
/**
|
|
4317
3479
|
* Whether the selection names chains and nothing else, which is the shape
|
|
4318
3480
|
* shown by dimming the chains around it rather than by focusing it.
|
|
@@ -4333,7 +3495,7 @@ function useSelectionFocus({ hiddenChains, isReady, pluginRef, selection, setCli
|
|
|
4333
3495
|
return;
|
|
4334
3496
|
}
|
|
4335
3497
|
const chosen = selection;
|
|
4336
|
-
const loci = lociForSelection(plugin, chosen, hiddenChains);
|
|
3498
|
+
const loci = require_renderStructureImage.lociForSelection(plugin, chosen, hiddenChains);
|
|
4337
3499
|
if (!loci) {
|
|
4338
3500
|
clearFocus(plugin, setClipRatio, false);
|
|
4339
3501
|
return;
|
|
@@ -4401,34 +3563,34 @@ function useSelectionReadout({ hoveredResidue, isReady, plddt, pluginRef, residu
|
|
|
4401
3563
|
(0, react.useEffect)(() => {
|
|
4402
3564
|
var _residuesByChainRef$c;
|
|
4403
3565
|
const plugin = pluginRef.current;
|
|
4404
|
-
if (!plugin || !isReady || isEmptySelection(selection)) {
|
|
3566
|
+
if (!plugin || !isReady || require_renderStructureImage.isEmptySelection(selection)) {
|
|
4405
3567
|
setResolved(null);
|
|
4406
3568
|
return;
|
|
4407
3569
|
}
|
|
4408
3570
|
const chosen = selection;
|
|
4409
|
-
const residues = selectionResidues(chosen, (_residuesByChainRef$c = residuesByChainRef.current) !== null && _residuesByChainRef$c !== void 0 ? _residuesByChainRef$c : /* @__PURE__ */ new Map());
|
|
3571
|
+
const residues = require_renderStructureImage.selectionResidues(chosen, (_residuesByChainRef$c = residuesByChainRef.current) !== null && _residuesByChainRef$c !== void 0 ? _residuesByChainRef$c : /* @__PURE__ */ new Map());
|
|
4410
3572
|
if (residues.length === 0) {
|
|
4411
3573
|
setResolved(null);
|
|
4412
3574
|
return;
|
|
4413
3575
|
}
|
|
4414
3576
|
if (residues.length > 1) {
|
|
4415
3577
|
setResolved({
|
|
4416
|
-
label: selectionLabel(chosen, residues.length),
|
|
3578
|
+
label: require_renderStructureImage.selectionLabel(chosen, residues.length),
|
|
4417
3579
|
residues
|
|
4418
3580
|
});
|
|
4419
3581
|
return;
|
|
4420
3582
|
}
|
|
4421
|
-
const loci = lociForSelection(plugin, chosen);
|
|
3583
|
+
const loci = require_renderStructureImage.lociForSelection(plugin, chosen);
|
|
4422
3584
|
const residue = loci && residueRefFromLoci(loci);
|
|
4423
3585
|
setResolved(residue ? {
|
|
4424
3586
|
label: residueLabel(residue),
|
|
4425
3587
|
residues
|
|
4426
3588
|
} : null);
|
|
4427
|
-
}, [isReady, selection && selectionKey(selection)]);
|
|
3589
|
+
}, [isReady, selection && require_renderStructureImage.selectionKey(selection)]);
|
|
4428
3590
|
const buildReadout = (0, react.useCallback)((label, residues) => ({
|
|
4429
3591
|
label,
|
|
4430
|
-
plddt: meanOverResidues(residues, (i) => plddt === null || plddt === void 0 ? void 0 : plddt[i]),
|
|
4431
|
-
value: meanOverResidues(residues, (i) => residueOverlay === null || residueOverlay === void 0 ? void 0 : residueOverlay.values.get(i)),
|
|
3592
|
+
plddt: require_renderStructureImage.meanOverResidues(residues, (i) => plddt === null || plddt === void 0 ? void 0 : plddt[i]),
|
|
3593
|
+
value: require_renderStructureImage.meanOverResidues(residues, (i) => residueOverlay === null || residueOverlay === void 0 ? void 0 : residueOverlay.values.get(i)),
|
|
4432
3594
|
residueCount: residues.length
|
|
4433
3595
|
}), [plddt, residueOverlay]);
|
|
4434
3596
|
return {
|
|
@@ -4437,52 +3599,6 @@ function useSelectionReadout({ hoveredResidue, isReady, plddt, pluginRef, residu
|
|
|
4437
3599
|
};
|
|
4438
3600
|
}
|
|
4439
3601
|
//#endregion
|
|
4440
|
-
//#region src/core/ProteinStructureViewer/hooks/useStructureColoring.ts
|
|
4441
|
-
/**
|
|
4442
|
-
* Decides what is coloring the structure and keeps that theme's data current.
|
|
4443
|
-
*
|
|
4444
|
-
* Three themes compete for the same representations, in a fixed order: a
|
|
4445
|
-
* residue value overlay, then pLDDT scores, then per-chain colors. Only one
|
|
4446
|
-
* can be applied, so the precedence lives here rather than being spread across
|
|
4447
|
-
* the hooks that own each theme's data - and the legend resolves what to
|
|
4448
|
-
* describe the same way, so the two cannot disagree about what is on screen.
|
|
4449
|
-
*
|
|
4450
|
-
* Recoloring happens in place: the camera is left where it is, so selecting or
|
|
4451
|
-
* switching an overlay does not zoom back out to the default view.
|
|
4452
|
-
*/
|
|
4453
|
-
function useStructureColoring({ chainColorThemeRef, chainColors, hasPlddt, isReady, mode, overlay, pluginRef, residueValueThemeRef }) {
|
|
4454
|
-
(0, react.useEffect)(() => {
|
|
4455
|
-
const plugin = pluginRef.current;
|
|
4456
|
-
const residueTheme = residueValueThemeRef.current;
|
|
4457
|
-
const chainTheme = chainColorThemeRef.current;
|
|
4458
|
-
if (!plugin || !residueTheme || !chainTheme || !isReady) return;
|
|
4459
|
-
chainTheme.setState(chainColors);
|
|
4460
|
-
if (overlay) {
|
|
4461
|
-
var _overlay$colorScale, _overlay$min;
|
|
4462
|
-
residueTheme.setState({
|
|
4463
|
-
colorScale: (_overlay$colorScale = overlay.colorScale) !== null && _overlay$colorScale !== void 0 ? _overlay$colorScale : require_colorScales.PLASMA_COLOR_SCALE,
|
|
4464
|
-
max: overlay.max,
|
|
4465
|
-
min: (_overlay$min = overlay.min) !== null && _overlay$min !== void 0 ? _overlay$min : 0,
|
|
4466
|
-
mode,
|
|
4467
|
-
values: overlay.values
|
|
4468
|
-
});
|
|
4469
|
-
applyColorTheme(plugin, RESIDUE_VALUE_THEME_NAME);
|
|
4470
|
-
return;
|
|
4471
|
-
}
|
|
4472
|
-
residueTheme.setState({
|
|
4473
|
-
mode,
|
|
4474
|
-
values: /* @__PURE__ */ new Map()
|
|
4475
|
-
});
|
|
4476
|
-
applyColorTheme(plugin, hasPlddt ? PLDDT_THEME_NAME : FALLBACK_THEME_NAME);
|
|
4477
|
-
}, [
|
|
4478
|
-
overlay,
|
|
4479
|
-
hasPlddt,
|
|
4480
|
-
isReady,
|
|
4481
|
-
mode,
|
|
4482
|
-
chainColors
|
|
4483
|
-
]);
|
|
4484
|
-
}
|
|
4485
|
-
//#endregion
|
|
4486
3602
|
//#region src/core/ProteinStructureViewer/ProteinStructureViewer.types.ts
|
|
4487
3603
|
/** Pixel dimensions each resolution produces, for documentation and labels. */
|
|
4488
3604
|
const DOWNLOAD_RESOLUTIONS = {
|
|
@@ -4496,18 +3612,30 @@ const DOWNLOAD_RESOLUTIONS = {
|
|
|
4496
3612
|
const _excluded = [
|
|
4497
3613
|
"backgroundColor",
|
|
4498
3614
|
"chainColors",
|
|
3615
|
+
"colorBy",
|
|
4499
3616
|
"disableChainHighlightOnHover",
|
|
4500
3617
|
"download",
|
|
4501
3618
|
"hiddenChains",
|
|
3619
|
+
"highlights",
|
|
3620
|
+
"initialCamera",
|
|
3621
|
+
"onCameraChange",
|
|
4502
3622
|
"onChainVisibilityChange",
|
|
4503
3623
|
"onChainsChange",
|
|
4504
3624
|
"molstarSpec",
|
|
3625
|
+
"onDispose",
|
|
3626
|
+
"onError",
|
|
3627
|
+
"onReady",
|
|
4505
3628
|
"onResidueClick",
|
|
4506
3629
|
"onResidueHover",
|
|
4507
3630
|
"onSelectionChange",
|
|
3631
|
+
"onStructureLoad",
|
|
3632
|
+
"orientation",
|
|
3633
|
+
"projection",
|
|
3634
|
+
"representation",
|
|
4508
3635
|
"structure",
|
|
4509
3636
|
"plddt",
|
|
4510
3637
|
"residueOverlay",
|
|
3638
|
+
"sceneMode",
|
|
4511
3639
|
"selection",
|
|
4512
3640
|
"sequenceViewerBackgroundColor",
|
|
4513
3641
|
"showAxes",
|
|
@@ -4534,16 +3662,26 @@ const PLDDT_SCALE_LABEL = "pLDDT";
|
|
|
4534
3662
|
const DEFAULT_OVERLAY_LABEL = "Value";
|
|
4535
3663
|
/** Readout slot label used when an overlay does not name one. */
|
|
4536
3664
|
const DEFAULT_READOUT_LABEL = "Value";
|
|
3665
|
+
const NO_SCALE = {
|
|
3666
|
+
scale: null,
|
|
3667
|
+
scaleLabel: void 0,
|
|
3668
|
+
scaleMax: null,
|
|
3669
|
+
scaleTooltip: void 0,
|
|
3670
|
+
scaleTooltipProps: void 0,
|
|
3671
|
+
valueLabel: void 0
|
|
3672
|
+
};
|
|
4537
3673
|
/**
|
|
4538
3674
|
* Chooses what the legend describes, following whatever is actually coloring
|
|
4539
|
-
* the structure: the overlay when
|
|
4540
|
-
*
|
|
4541
|
-
*
|
|
4542
|
-
*
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
3675
|
+
* the structure: the overlay when it is painting, the pLDDT bands when they
|
|
3676
|
+
* are, and nothing at all for chain coloring - which no per-residue scale
|
|
3677
|
+
* describes, so a key would be labelling colors that are not on screen. An
|
|
3678
|
+
* overlay named by `colorBy` but not supplied paints everything neutral, and
|
|
3679
|
+
* has no key either.
|
|
3680
|
+
*/
|
|
3681
|
+
function resolveScaleProps(colorBy, overlay) {
|
|
3682
|
+
if (colorBy === "overlay") {
|
|
4546
3683
|
var _overlay$colorScale, _overlay$label, _overlay$min, _overlay$readoutLabel;
|
|
3684
|
+
if (!overlay) return NO_SCALE;
|
|
4547
3685
|
return {
|
|
4548
3686
|
scale: (_overlay$colorScale = overlay.colorScale) !== null && _overlay$colorScale !== void 0 ? _overlay$colorScale : require_colorScales.PLASMA_COLOR_SCALE,
|
|
4549
3687
|
scaleLabel: (_overlay$label = overlay.label) !== null && _overlay$label !== void 0 ? _overlay$label : DEFAULT_OVERLAY_LABEL,
|
|
@@ -4554,48 +3692,34 @@ function resolveScaleProps(overlay, hasPlddt) {
|
|
|
4554
3692
|
valueLabel: (_overlay$readoutLabel = overlay.readoutLabel) !== null && _overlay$readoutLabel !== void 0 ? _overlay$readoutLabel : DEFAULT_READOUT_LABEL
|
|
4555
3693
|
};
|
|
4556
3694
|
}
|
|
4557
|
-
if (
|
|
4558
|
-
scale: PLDDT_COLOR_SCALE,
|
|
4559
|
-
scaleLabel: PLDDT_SCALE_LABEL
|
|
4560
|
-
|
|
4561
|
-
|
|
4562
|
-
scaleTooltipProps: void 0,
|
|
4563
|
-
valueLabel: void 0
|
|
4564
|
-
};
|
|
4565
|
-
return {
|
|
4566
|
-
scale: null,
|
|
4567
|
-
scaleLabel: void 0,
|
|
4568
|
-
scaleMax: null,
|
|
4569
|
-
scaleTooltip: void 0,
|
|
4570
|
-
scaleTooltipProps: void 0,
|
|
4571
|
-
valueLabel: void 0
|
|
4572
|
-
};
|
|
3695
|
+
if (colorBy === "plddt") return require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, NO_SCALE), {}, {
|
|
3696
|
+
scale: require_renderStructureImage.PLDDT_COLOR_SCALE,
|
|
3697
|
+
scaleLabel: PLDDT_SCALE_LABEL
|
|
3698
|
+
});
|
|
3699
|
+
return NO_SCALE;
|
|
4573
3700
|
}
|
|
4574
3701
|
/**
|
|
4575
3702
|
* Interactive 3D protein structure viewer built on Mol*, with a sequence panel,
|
|
4576
3703
|
* pLDDT confidence coloring, per-residue value overlays, and a stats legend.
|
|
4577
3704
|
*
|
|
4578
|
-
* The
|
|
4579
|
-
*
|
|
4580
|
-
*
|
|
4581
|
-
*
|
|
3705
|
+
* The selection drives the camera: clicking a residue zooms in on it, and
|
|
3706
|
+
* clearing the selection zooms back out. It is controlled when `selection` is
|
|
3707
|
+
* passed and owned internally when it is not, so a click zooms without the
|
|
3708
|
+
* consumer holding any state. Hover state is always owned internally, so the
|
|
3709
|
+
* legend can show a live readout without the consumer round-tripping every
|
|
3710
|
+
* pointer move.
|
|
4582
3711
|
*/
|
|
4583
3712
|
const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
4584
3713
|
var _selection$chains;
|
|
4585
|
-
const { backgroundColor, chainColors: chainColorOverrides, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, onChainVisibilityChange, onChainsChange, molstarSpec, onResidueClick, onResidueHover, onSelectionChange, structure, plddt, residueOverlay,
|
|
3714
|
+
const { backgroundColor, chainColors: chainColorOverrides, colorBy: colorByProp, disableChainHighlightOnHover = false, download, hiddenChains: hiddenChainsProp, highlights, initialCamera, onCameraChange, onChainVisibilityChange, onChainsChange, molstarSpec, onDispose, onError, onReady, onResidueClick, onResidueHover, onSelectionChange, onStructureLoad, orientation, projection, representation = "cartoon", structure, plddt, residueOverlay, sceneMode = "managed", selection: selectionProp, sequenceViewerBackgroundColor, showAxes = true, showChainLegend = true, showLegend = true, showSequenceViewer = true, stats } = props, rest = require_objectSpread2._objectWithoutProperties(props, _excluded);
|
|
4586
3715
|
const theme = (0, _mui_material_styles.useTheme)();
|
|
4587
3716
|
const mode = (0, _czi_sds_components.getMode)({ theme });
|
|
4588
3717
|
const semanticColors = (0, _czi_sds_components.getSemanticColors)({ theme });
|
|
4589
3718
|
const pluginMountRef = (0, react.useRef)(null);
|
|
4590
|
-
const
|
|
4591
|
-
const processedStructure = (0, react.useMemo)(() => hasPlddt ? injectPlddt(structure, plddt) : structure, [
|
|
4592
|
-
structure,
|
|
4593
|
-
plddt,
|
|
4594
|
-
hasPlddt
|
|
4595
|
-
]);
|
|
3719
|
+
const colorBy = require_renderStructureImage.resolveColorBy(colorByProp, residueOverlay, plddt);
|
|
4596
3720
|
const bgColor = (0, react.useMemo)(() => {
|
|
4597
3721
|
var _semanticColors$base;
|
|
4598
|
-
return themeColor(backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : semanticColors === null || semanticColors === void 0 || (_semanticColors$base = semanticColors.base) === null || _semanticColors$base === void 0 ? void 0 : _semanticColors$base.backgroundPrimary, FALLBACK_BACKGROUND_COLOR[mode]);
|
|
3722
|
+
return require_renderStructureImage.themeColor(backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : semanticColors === null || semanticColors === void 0 || (_semanticColors$base = semanticColors.base) === null || _semanticColors$base === void 0 ? void 0 : _semanticColors$base.backgroundPrimary, FALLBACK_BACKGROUND_COLOR[mode]);
|
|
4599
3723
|
}, [
|
|
4600
3724
|
backgroundColor,
|
|
4601
3725
|
mode,
|
|
@@ -4603,16 +3727,21 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4603
3727
|
]);
|
|
4604
3728
|
const edgeColor = (0, react.useMemo)(() => {
|
|
4605
3729
|
var _semanticColors$base2;
|
|
4606
|
-
return themeColor(semanticColors === null || semanticColors === void 0 || (_semanticColors$base2 = semanticColors.base) === null || _semanticColors$base2 === void 0 ? void 0 : _semanticColors$base2.borderPrimary, FALLBACK_EDGE_COLOR);
|
|
3730
|
+
return require_renderStructureImage.themeColor(semanticColors === null || semanticColors === void 0 || (_semanticColors$base2 = semanticColors.base) === null || _semanticColors$base2 === void 0 ? void 0 : _semanticColors$base2.borderPrimary, FALLBACK_EDGE_COLOR);
|
|
4607
3731
|
}, [semanticColors]);
|
|
4608
3732
|
const highlightColor = (0, react.useMemo)(() => {
|
|
4609
3733
|
var _semanticColors$base3;
|
|
4610
|
-
return themeColor(semanticColors === null || semanticColors === void 0 || (_semanticColors$base3 = semanticColors.base) === null || _semanticColors$base3 === void 0 ? void 0 : _semanticColors$base3.fillSelected, FALLBACK_HIGHLIGHT_COLOR);
|
|
3734
|
+
return require_renderStructureImage.themeColor(semanticColors === null || semanticColors === void 0 || (_semanticColors$base3 = semanticColors.base) === null || _semanticColors$base3 === void 0 ? void 0 : _semanticColors$base3.fillSelected, FALLBACK_HIGHLIGHT_COLOR);
|
|
4611
3735
|
}, [semanticColors]);
|
|
4612
3736
|
const { handleResidueHover, hoveredResidue } = useResidueHoverState(onResidueHover);
|
|
3737
|
+
const { changeSelection, selection } = useSelection({
|
|
3738
|
+
onSelectionChange,
|
|
3739
|
+
selection: selectionProp,
|
|
3740
|
+
structure
|
|
3741
|
+
});
|
|
4613
3742
|
const handleSelectionClear = (0, react.useCallback)(() => {
|
|
4614
|
-
|
|
4615
|
-
}, [
|
|
3743
|
+
changeSelection(null);
|
|
3744
|
+
}, [changeSelection]);
|
|
4616
3745
|
/**
|
|
4617
3746
|
* Chains the selection covers whole, which is what makes a chain caption a
|
|
4618
3747
|
* toggle rather than a one-way switch. Keyed on its contents because the
|
|
@@ -4631,10 +3760,9 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4631
3760
|
* than undoing itself.
|
|
4632
3761
|
*/
|
|
4633
3762
|
const handleChainSelect = (0, react.useCallback)((chainId) => {
|
|
4634
|
-
var _selection$chains2, _selection$residues;
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
}, [onSelectionChange, selection]);
|
|
3763
|
+
var _selection$chains2, _selection$residues, _selection$addresses;
|
|
3764
|
+
changeSelection((selection === null || selection === void 0 || (_selection$chains2 = selection.chains) === null || _selection$chains2 === void 0 ? void 0 : _selection$chains2.length) === 1 && selection.chains[0] === chainId && !((_selection$residues = selection.residues) === null || _selection$residues === void 0 ? void 0 : _selection$residues.length) && !((_selection$addresses = selection.addresses) === null || _selection$addresses === void 0 ? void 0 : _selection$addresses.length) ? null : { chains: [chainId] });
|
|
3765
|
+
}, [changeSelection, selection]);
|
|
4638
3766
|
/**
|
|
4639
3767
|
* Dims every other chain while a chain's name is pointed at, in the legend
|
|
4640
3768
|
* or in the sequence panel's captions, and leaves the selected chain lit
|
|
@@ -4657,13 +3785,11 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4657
3785
|
hiddenChains: hiddenChainsProp,
|
|
4658
3786
|
onChainVisibilityChange
|
|
4659
3787
|
});
|
|
4660
|
-
const {
|
|
3788
|
+
const { addressIndexRef, chains: loadedChains, framedOrientationRef, isReady, loadCount, pluginRef, residuesByChainRef, sceneRef, sceneVersion, setClipRatio } = useMolstarPlugin({
|
|
4661
3789
|
backgroundColor: bgColor,
|
|
4662
|
-
chainColors,
|
|
4663
3790
|
containerRef: pluginMountRef,
|
|
4664
3791
|
download,
|
|
4665
3792
|
edgeColor,
|
|
4666
|
-
hasPlddt,
|
|
4667
3793
|
hiddenChains,
|
|
4668
3794
|
highlightColor,
|
|
4669
3795
|
mode,
|
|
@@ -4671,23 +3797,52 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4671
3797
|
onChainHover: highlightChain,
|
|
4672
3798
|
onChainSelect: handleChainSelect,
|
|
4673
3799
|
onChainToggle: toggleChain,
|
|
3800
|
+
onDispose,
|
|
3801
|
+
onError,
|
|
3802
|
+
onReady,
|
|
4674
3803
|
onResidueClick,
|
|
4675
3804
|
onResidueHover: handleResidueHover,
|
|
4676
|
-
onSelectionChange,
|
|
3805
|
+
onSelectionChange: changeSelection,
|
|
4677
3806
|
onSelectionClear: handleSelectionClear,
|
|
3807
|
+
onStructureLoad,
|
|
3808
|
+
sceneProps: {
|
|
3809
|
+
chainColors: chainColorOverrides,
|
|
3810
|
+
colorBy: colorByProp,
|
|
3811
|
+
hiddenChains,
|
|
3812
|
+
highlights,
|
|
3813
|
+
initialCamera,
|
|
3814
|
+
mode,
|
|
3815
|
+
orientation,
|
|
3816
|
+
overlay: residueOverlay,
|
|
3817
|
+
plddt,
|
|
3818
|
+
projection,
|
|
3819
|
+
representation
|
|
3820
|
+
},
|
|
3821
|
+
sceneMode,
|
|
4678
3822
|
selectedChains,
|
|
4679
3823
|
sequenceViewerBackgroundColor,
|
|
4680
3824
|
showAxes,
|
|
4681
3825
|
showSequenceViewer,
|
|
4682
|
-
structure
|
|
3826
|
+
structure
|
|
4683
3827
|
});
|
|
4684
3828
|
highlightChainRef.current = useChainHighlight({
|
|
4685
3829
|
disabled: disableChainHighlightOnHover,
|
|
4686
3830
|
hiddenChains,
|
|
4687
3831
|
loadCount,
|
|
4688
3832
|
pluginRef,
|
|
3833
|
+
sceneVersion,
|
|
4689
3834
|
selectedChains
|
|
4690
3835
|
});
|
|
3836
|
+
useCamera({
|
|
3837
|
+
framedOrientationRef,
|
|
3838
|
+
highlights,
|
|
3839
|
+
isReady,
|
|
3840
|
+
onCameraChange,
|
|
3841
|
+
orientation,
|
|
3842
|
+
pluginRef,
|
|
3843
|
+
projection,
|
|
3844
|
+
sceneRef
|
|
3845
|
+
});
|
|
4691
3846
|
(0, react.useEffect)(() => {
|
|
4692
3847
|
setChains(loadedChains);
|
|
4693
3848
|
}, [loadedChains]);
|
|
@@ -4697,21 +3852,19 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4697
3852
|
var _onChainsChangeRef$cu;
|
|
4698
3853
|
(_onChainsChangeRef$cu = onChainsChangeRef.current) === null || _onChainsChangeRef$cu === void 0 || _onChainsChangeRef$cu.call(onChainsChangeRef, loadedChains);
|
|
4699
3854
|
}, [loadedChains]);
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
residueValueThemeRef
|
|
4709
|
-
});
|
|
3855
|
+
const focusedSelection = (0, react.useMemo)(() => {
|
|
3856
|
+
var _addressIndexRef$curr;
|
|
3857
|
+
return require_renderStructureImage.resolveSelectionAddresses(selection, (_addressIndexRef$curr = addressIndexRef.current) !== null && _addressIndexRef$curr !== void 0 ? _addressIndexRef$curr : /* @__PURE__ */ new Map());
|
|
3858
|
+
}, [
|
|
3859
|
+
selection,
|
|
3860
|
+
require_renderStructureImage.residueAddressesKey(selection === null || selection === void 0 ? void 0 : selection.addresses),
|
|
3861
|
+
loadCount
|
|
3862
|
+
]);
|
|
4710
3863
|
useSelectionFocus({
|
|
4711
3864
|
hiddenChains,
|
|
4712
3865
|
isReady,
|
|
4713
3866
|
pluginRef,
|
|
4714
|
-
selection,
|
|
3867
|
+
selection: focusedSelection,
|
|
4715
3868
|
setClipRatio
|
|
4716
3869
|
});
|
|
4717
3870
|
const { hoveredReadout, selectedReadout } = useSelectionReadout({
|
|
@@ -4721,9 +3874,9 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4721
3874
|
pluginRef,
|
|
4722
3875
|
residueOverlay,
|
|
4723
3876
|
residuesByChainRef,
|
|
4724
|
-
selection
|
|
3877
|
+
selection: focusedSelection
|
|
4725
3878
|
});
|
|
4726
|
-
const scaleProps = (0, react.useMemo)(() => resolveScaleProps(
|
|
3879
|
+
const scaleProps = (0, react.useMemo)(() => resolveScaleProps(colorBy, residueOverlay), [colorBy, residueOverlay]);
|
|
4727
3880
|
/**
|
|
4728
3881
|
* What a chain's swatch can show follows whatever is painting the
|
|
4729
3882
|
* structure, in the same order the color key resolves in.
|
|
@@ -4735,13 +3888,13 @@ const ProteinStructureViewer = (0, react.forwardRef)((props, ref) => {
|
|
|
4735
3888
|
* a swatch at all, so the rows keep their labels and toggles and the color
|
|
4736
3889
|
* key beside them does the describing.
|
|
4737
3890
|
*/
|
|
4738
|
-
const chainColoringActive =
|
|
4739
|
-
const plddtColoringActive =
|
|
3891
|
+
const chainColoringActive = colorBy === "chain";
|
|
3892
|
+
const plddtColoringActive = colorBy === "plddt";
|
|
4740
3893
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ViewerRoot, require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({
|
|
4741
3894
|
ref,
|
|
4742
3895
|
showSequenceViewer
|
|
4743
3896
|
}, rest), {}, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PluginMount, { ref: pluginMountRef }), showLegend && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StructureLegend, require_objectSpread2._objectSpread2({
|
|
4744
|
-
chainBandColors: plddtColoringActive ? PLDDT_BAND_COLORS : void 0,
|
|
3897
|
+
chainBandColors: plddtColoringActive ? require_renderStructureImage.PLDDT_BAND_COLORS : void 0,
|
|
4745
3898
|
chainColors: chainColoringActive ? chainColors : void 0,
|
|
4746
3899
|
chains: showChainLegend ? chains : [],
|
|
4747
3900
|
hiddenChains,
|
|
@@ -4763,39 +3916,9 @@ Object.defineProperty(exports, "DOWNLOAD_RESOLUTIONS", {
|
|
|
4763
3916
|
return DOWNLOAD_RESOLUTIONS;
|
|
4764
3917
|
}
|
|
4765
3918
|
});
|
|
4766
|
-
Object.defineProperty(exports, "PLDDT_COLOR_SCALE", {
|
|
4767
|
-
enumerable: true,
|
|
4768
|
-
get: function() {
|
|
4769
|
-
return PLDDT_COLOR_SCALE;
|
|
4770
|
-
}
|
|
4771
|
-
});
|
|
4772
3919
|
Object.defineProperty(exports, "ProteinStructureViewer_default", {
|
|
4773
3920
|
enumerable: true,
|
|
4774
3921
|
get: function() {
|
|
4775
3922
|
return ProteinStructureViewer_default;
|
|
4776
3923
|
}
|
|
4777
3924
|
});
|
|
4778
|
-
Object.defineProperty(exports, "detectStructureFormat", {
|
|
4779
|
-
enumerable: true,
|
|
4780
|
-
get: function() {
|
|
4781
|
-
return detectStructureFormat;
|
|
4782
|
-
}
|
|
4783
|
-
});
|
|
4784
|
-
Object.defineProperty(exports, "injectPlddt", {
|
|
4785
|
-
enumerable: true,
|
|
4786
|
-
get: function() {
|
|
4787
|
-
return injectPlddt;
|
|
4788
|
-
}
|
|
4789
|
-
});
|
|
4790
|
-
Object.defineProperty(exports, "injectPlddtIntoMmcif", {
|
|
4791
|
-
enumerable: true,
|
|
4792
|
-
get: function() {
|
|
4793
|
-
return injectPlddtIntoMmcif;
|
|
4794
|
-
}
|
|
4795
|
-
});
|
|
4796
|
-
Object.defineProperty(exports, "injectPlddtIntoPdb", {
|
|
4797
|
-
enumerable: true,
|
|
4798
|
-
get: function() {
|
|
4799
|
-
return injectPlddtIntoPdb;
|
|
4800
|
-
}
|
|
4801
|
-
});
|