@aics/vole-app 2.12.0 → 2.13.1
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/es/aics-image-viewer/components/App/ChannelUpdater.js +11 -12
- package/es/aics-image-viewer/components/App/index.js +216 -466
- package/es/aics-image-viewer/components/AxisClipSliders/index.js +12 -12
- package/es/aics-image-viewer/components/BottomPanel/index.js +12 -28
- package/es/aics-image-viewer/components/CellViewerCanvasWrapper/index.js +3 -12
- package/es/aics-image-viewer/components/ChannelsWidgetRow/index.js +2 -2
- package/es/aics-image-viewer/components/ChannelsWidgetRow/styles.css +5 -1
- package/es/aics-image-viewer/components/ErrorAlert/index.js +1 -1
- package/es/aics-image-viewer/components/TfEditor/index.js +75 -35
- package/es/aics-image-viewer/components/TfEditor/styles.css +2 -3
- package/es/aics-image-viewer/components/Toolbar/ViewModeRadioButtons.js +1 -1
- package/es/aics-image-viewer/components/Toolbar/index.js +34 -30
- package/es/aics-image-viewer/components/ViewerStateProvider/index.js +11 -13
- package/es/aics-image-viewer/components/useVolume.js +327 -0
- package/es/aics-image-viewer/shared/constants.js +10 -1
- package/es/aics-image-viewer/shared/utils/hooks.js +3 -26
- package/es/aics-image-viewer/shared/utils/sliderValuesToImageValues.js +15 -9
- package/es/aics-image-viewer/shared/utils/viewerState.js +1 -1
- package/package.json +3 -2
- package/type-declarations/aics-image-viewer/components/BottomPanel/index.d.ts +2 -2
- package/type-declarations/aics-image-viewer/components/CellViewerCanvasWrapper/index.d.ts +2 -2
- package/type-declarations/aics-image-viewer/components/TfEditor/index.d.ts +1 -1
- package/type-declarations/aics-image-viewer/components/Toolbar/index.d.ts +1 -1
- package/type-declarations/aics-image-viewer/components/ViewerStateProvider/types.d.ts +1 -1
- package/type-declarations/aics-image-viewer/components/useVolume.d.ts +49 -0
- package/type-declarations/aics-image-viewer/shared/constants.d.ts +4 -1
- package/type-declarations/aics-image-viewer/shared/utils/hooks.d.ts +0 -2
- package/type-declarations/aics-image-viewer/shared/utils/sliderValuesToImageValues.d.ts +6 -3
|
@@ -43,7 +43,7 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
43
43
|
className: "axis-slider-container"
|
|
44
44
|
}, /*#__PURE__*/React.createElement("span", {
|
|
45
45
|
className: "slider-name"
|
|
46
|
-
}, label), max
|
|
46
|
+
}, label), max <= min ? /*#__PURE__*/React.createElement("i", null, "No values to adjust") : /*#__PURE__*/React.createElement("span", {
|
|
47
47
|
className: "axis-slider"
|
|
48
48
|
}, /*#__PURE__*/React.createElement(SmarterSlider, {
|
|
49
49
|
className: isRange ? "" : "slider-single-handle",
|
|
@@ -77,7 +77,7 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
77
77
|
onChange: wrappedOnChange,
|
|
78
78
|
onStart: onStart,
|
|
79
79
|
onEnd: onEnd
|
|
80
|
-
})), max >
|
|
80
|
+
})), max > min && /*#__PURE__*/React.createElement("span", {
|
|
81
81
|
className: "slider-values"
|
|
82
82
|
}, /*#__PURE__*/React.createElement(NumericInput, {
|
|
83
83
|
min: min,
|
|
@@ -97,6 +97,9 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
97
97
|
};
|
|
98
98
|
/** Wrapper around `SliderRow` that adds a play button and accounts for the case where not all of an axis is loaded */
|
|
99
99
|
var PlaySliderRow = function PlaySliderRow(props) {
|
|
100
|
+
var onChange = props.onChange,
|
|
101
|
+
onStart = props.onStart,
|
|
102
|
+
onEnd = props.onEnd;
|
|
100
103
|
// In partially-loaded axes, stores the displayed value of the slider while the user is sliding it
|
|
101
104
|
var _useState = useState(props.val),
|
|
102
105
|
_useState2 = _slicedToArray(_useState, 2),
|
|
@@ -108,27 +111,24 @@ var PlaySliderRow = function PlaySliderRow(props) {
|
|
|
108
111
|
sliderHeld = _useState4[0],
|
|
109
112
|
setSliderHeld = _useState4[1];
|
|
110
113
|
var wrappedOnChange = useCallback(function (_ref2) {
|
|
111
|
-
var _props$onChange;
|
|
112
114
|
var _ref3 = _slicedToArray(_ref2, 1),
|
|
113
115
|
val = _ref3[0];
|
|
114
|
-
return
|
|
115
|
-
}, [
|
|
116
|
+
return onChange === null || onChange === void 0 ? void 0 : onChange(val);
|
|
117
|
+
}, [onChange]);
|
|
116
118
|
var wrappedSetValReadout = useCallback(function (_ref4) {
|
|
117
119
|
var _ref5 = _slicedToArray(_ref4, 1),
|
|
118
120
|
val = _ref5[0];
|
|
119
121
|
return setValReadout(val);
|
|
120
122
|
}, []);
|
|
121
123
|
var wrappedOnStart = useCallback(function () {
|
|
122
|
-
var _props$onStart;
|
|
123
124
|
setValReadout(props.val);
|
|
124
125
|
setSliderHeld(true);
|
|
125
|
-
|
|
126
|
-
}, [props.
|
|
126
|
+
onStart === null || onStart === void 0 || onStart();
|
|
127
|
+
}, [onStart, props.val]);
|
|
127
128
|
var wrappedOnEnd = useCallback(function () {
|
|
128
|
-
var _props$onEnd;
|
|
129
129
|
setSliderHeld(false);
|
|
130
|
-
|
|
131
|
-
}, [
|
|
130
|
+
onEnd === null || onEnd === void 0 || onEnd();
|
|
131
|
+
}, [onEnd]);
|
|
132
132
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SliderRow, {
|
|
133
133
|
label: props.label,
|
|
134
134
|
vals: [props.val],
|
|
@@ -181,7 +181,7 @@ var AxisClipSliders = function AxisClipSliders(props) {
|
|
|
181
181
|
// Pause when view mode or volume size has changed
|
|
182
182
|
useEffect(function () {
|
|
183
183
|
return props.playControls.pause();
|
|
184
|
-
}, [props.mode, props.image]);
|
|
184
|
+
}, [props.mode, props.image, props.playControls]);
|
|
185
185
|
var handlePlayPause = function handlePlayPause(axis, willPlay) {
|
|
186
186
|
if (willPlay) {
|
|
187
187
|
props.playControls.play(axis);
|
|
@@ -5,41 +5,26 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
|
|
|
5
5
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
6
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
7
|
import { Button, Drawer } from "antd";
|
|
8
|
-
import React, {
|
|
8
|
+
import React, { useCallback, useState } from "react";
|
|
9
9
|
import ViewerIcon from "../shared/ViewerIcon";
|
|
10
10
|
import "./styles.css";
|
|
11
11
|
var BottomPanel = function BottomPanel(_ref) {
|
|
12
12
|
var children = _ref.children,
|
|
13
|
+
openProp = _ref.open,
|
|
13
14
|
title = _ref.title,
|
|
14
15
|
height = _ref.height,
|
|
15
|
-
|
|
16
|
-
onVisibleChangeEnd = _ref.onVisibleChangeEnd;
|
|
16
|
+
onOpenChange = _ref.onOpenChange;
|
|
17
17
|
var _useState = useState(true),
|
|
18
18
|
_useState2 = _slicedToArray(_useState, 2),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!isVisible) {
|
|
26
|
-
onVisibleChangeEnd === null || onVisibleChangeEnd === void 0 || onVisibleChangeEnd(isVisible);
|
|
19
|
+
openState = _useState2[0],
|
|
20
|
+
setOpenState = _useState2[1];
|
|
21
|
+
var open = openProp !== null && openProp !== void 0 ? openProp : openState;
|
|
22
|
+
var toggleDrawer = useCallback(function () {
|
|
23
|
+
if (openProp === undefined) {
|
|
24
|
+
setOpenState(!open);
|
|
27
25
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
// Treat changes in height as a change in visibility if the panel is open
|
|
31
|
-
useEffect(function () {
|
|
32
|
-
onVisibleChange === null || onVisibleChange === void 0 || onVisibleChange(isVisible);
|
|
33
|
-
window.setTimeout(function () {
|
|
34
|
-
return onVisibleChangeEnd === null || onVisibleChangeEnd === void 0 ? void 0 : onVisibleChangeEnd(isVisible);
|
|
35
|
-
}, 300);
|
|
36
|
-
}, [height]);
|
|
37
|
-
var toggleDrawer = function toggleDrawer() {
|
|
38
|
-
setIsVisible(!isVisible);
|
|
39
|
-
if (onVisibleChange) {
|
|
40
|
-
onVisibleChange(!isVisible);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
26
|
+
onOpenChange === null || onOpenChange === void 0 || onOpenChange(!open);
|
|
27
|
+
}, [open, openProp, onOpenChange]);
|
|
43
28
|
var optionsButton = /*#__PURE__*/React.createElement(Button, {
|
|
44
29
|
className: "options-button",
|
|
45
30
|
size: "small",
|
|
@@ -58,10 +43,9 @@ var BottomPanel = function BottomPanel(_ref) {
|
|
|
58
43
|
placement: "bottom",
|
|
59
44
|
closable: false,
|
|
60
45
|
getContainer: false,
|
|
61
|
-
open:
|
|
46
|
+
open: open,
|
|
62
47
|
mask: false,
|
|
63
48
|
title: optionsButton,
|
|
64
|
-
afterOpenChange: onVisibleChangeEnd,
|
|
65
49
|
height: height !== null && height !== void 0 ? height : 190
|
|
66
50
|
}, /*#__PURE__*/React.createElement("div", {
|
|
67
51
|
className: "drawer-body-wrapper"
|
|
@@ -16,13 +16,7 @@ var ViewerWrapper = function ViewerWrapper(props) {
|
|
|
16
16
|
var view3dviewerRef = /*#__PURE__*/React.createRef();
|
|
17
17
|
React.useEffect(function () {
|
|
18
18
|
view3dviewerRef.current.appendChild(props.view3d.getDOMElement());
|
|
19
|
-
|
|
20
|
-
}, []);
|
|
21
|
-
|
|
22
|
-
// TODO necessary?
|
|
23
|
-
React.useEffect(function () {
|
|
24
|
-
props.view3d.resize(null);
|
|
25
|
-
});
|
|
19
|
+
}, [props.view3d, view3dviewerRef]);
|
|
26
20
|
var renderOverlay = function renderOverlay() {
|
|
27
21
|
// Don't show spinner during playback - we may be constantly loading new data, it'll block the view!
|
|
28
22
|
var showSpinner = props.loadingImage && !props.playingAxis;
|
|
@@ -62,11 +56,8 @@ var ViewerWrapper = function ViewerWrapper(props) {
|
|
|
62
56
|
style: STYLES.view3d
|
|
63
57
|
}), /*#__PURE__*/React.createElement(BottomPanel, {
|
|
64
58
|
title: "Clipping",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
(_props$onClippingPane = props.onClippingPanelVisibleChange) === null || _props$onClippingPane === void 0 || _props$onClippingPane.call(props, visible, numTimesteps > 1, numScenes > 1, viewMode === ViewMode.threeD);
|
|
68
|
-
},
|
|
69
|
-
onVisibleChangeEnd: props.onClippingPanelVisibleChangeEnd,
|
|
59
|
+
open: props.clippingPanelOpen,
|
|
60
|
+
onOpenChange: props.onClippingPanelOpenChange,
|
|
70
61
|
height: clippingPanelTall ? CLIPPING_PANEL_HEIGHT_TALL : CLIPPING_PANEL_HEIGHT_DEFAULT
|
|
71
62
|
}, visibleControls.axisClipSliders && !!props.image && /*#__PURE__*/React.createElement(AxisClipSliders, {
|
|
72
63
|
mode: viewMode,
|
|
@@ -94,7 +94,7 @@ var ChannelsWidgetRow = function ChannelsWidgetRow(props) {
|
|
|
94
94
|
colorizeAlpha = channelState.colorizeAlpha,
|
|
95
95
|
useControlPoints = channelState.useControlPoints,
|
|
96
96
|
ramp = channelState.ramp,
|
|
97
|
-
|
|
97
|
+
plotMin = channelState.plotMin,
|
|
98
98
|
plotMax = channelState.plotMax;
|
|
99
99
|
return /*#__PURE__*/React.createElement(TfEditor, {
|
|
100
100
|
id: "TFEditor" + index,
|
|
@@ -107,7 +107,7 @@ var ChannelsWidgetRow = function ChannelsWidgetRow(props) {
|
|
|
107
107
|
colorizeAlpha: colorizeAlpha,
|
|
108
108
|
useControlPoints: useControlPoints,
|
|
109
109
|
ramp: ramp,
|
|
110
|
-
|
|
110
|
+
plotMin: plotMin,
|
|
111
111
|
plotMax: plotMax
|
|
112
112
|
});
|
|
113
113
|
};
|
|
@@ -30,9 +30,13 @@
|
|
|
30
30
|
display:flex;
|
|
31
31
|
justify-content:start;
|
|
32
32
|
align-items:end;
|
|
33
|
-
gap:
|
|
33
|
+
gap:5px;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
.channel-rows-list .ant-list-item.channel-row .button-row .ant-btn.ant-btn-sm{
|
|
37
|
+
padding:12px 8px;
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
.channel-rows-list .ant-list-item.channel-row.controls-closed .ant-btn-text:not(:focus-visible){
|
|
37
41
|
color:var(--color-controlpanel-text);
|
|
38
42
|
}
|
|
@@ -119,7 +119,7 @@ export var useErrorAlert = function useErrorAlert() {
|
|
|
119
119
|
return [].concat(_toConsumableArray(prev), [errorSeenCount]);
|
|
120
120
|
});
|
|
121
121
|
seenErrors.set(errorTitle, errorSeenCount);
|
|
122
|
-
}, []);
|
|
122
|
+
}, [seenErrors]);
|
|
123
123
|
var onSkipError = React.useCallback(function () {
|
|
124
124
|
setErrorList(function (prev) {
|
|
125
125
|
return prev.slice(1);
|
|
@@ -250,7 +250,9 @@ var clamp = function clamp(value, min, max) {
|
|
|
250
250
|
return Math.min(Math.max(value, min), max);
|
|
251
251
|
};
|
|
252
252
|
var TfEditor = function TfEditor(props) {
|
|
253
|
-
var changeChannelSetting = props.changeChannelSetting
|
|
253
|
+
var changeChannelSetting = props.changeChannelSetting,
|
|
254
|
+
plotMin = props.plotMin,
|
|
255
|
+
plotMax = props.plotMax;
|
|
254
256
|
var innerWidth = props.width - TFEDITOR_MARGINS.left - TFEDITOR_MARGINS.right;
|
|
255
257
|
var innerHeight = props.height - TFEDITOR_MARGINS.top - TFEDITOR_MARGINS.bottom;
|
|
256
258
|
var _useState = useState(null),
|
|
@@ -294,22 +296,27 @@ var TfEditor = function TfEditor(props) {
|
|
|
294
296
|
var _props$channelData = props.channelData,
|
|
295
297
|
rawMin = _props$channelData.rawMin,
|
|
296
298
|
rawMax = _props$channelData.rawMax,
|
|
297
|
-
dtype = _props$channelData.dtype
|
|
299
|
+
dtype = _props$channelData.dtype,
|
|
300
|
+
histogram = _props$channelData.histogram;
|
|
298
301
|
var typeRange = DTYPE_RANGE[dtype];
|
|
299
302
|
|
|
300
303
|
// d3 scales define the mapping between data and screen space (and do the heavy lifting of generating plot axes)
|
|
301
304
|
/** `xScale` is in raw intensity range, not U8 range. We use `u8ToAbsolute` and `absoluteToU8` to translate to U8. */
|
|
302
|
-
var
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
305
|
+
var xScale = useMemo(function () {
|
|
306
|
+
return d3.scaleLinear().domain([plotMin, plotMax]).range([0, innerWidth]);
|
|
307
|
+
}, [innerWidth, plotMin, plotMax]);
|
|
308
|
+
var plotMinU8 = useMemo(function () {
|
|
309
|
+
return absoluteToU8(plotMin, {
|
|
310
|
+
rawMin: rawMin,
|
|
311
|
+
rawMax: rawMax
|
|
312
|
+
});
|
|
313
|
+
}, [plotMin, rawMin, rawMax]);
|
|
314
|
+
var plotMaxU8 = useMemo(function () {
|
|
315
|
+
return absoluteToU8(plotMax, {
|
|
316
|
+
rawMin: rawMin,
|
|
317
|
+
rawMax: rawMax
|
|
318
|
+
});
|
|
319
|
+
}, [plotMax, rawMin, rawMax]);
|
|
313
320
|
var yScale = useMemo(function () {
|
|
314
321
|
return d3.scaleLinear().domain([0, 1]).range([innerHeight, 0]);
|
|
315
322
|
}, [innerHeight]);
|
|
@@ -454,12 +461,15 @@ var TfEditor = function TfEditor(props) {
|
|
|
454
461
|
var areaPath = useMemo(function () {
|
|
455
462
|
var _areaGenerator;
|
|
456
463
|
var areaGenerator = d3.area().x(function (d) {
|
|
457
|
-
return xScale(controlPointToAbsolute(d,
|
|
464
|
+
return xScale(controlPointToAbsolute(d, {
|
|
465
|
+
rawMin: rawMin,
|
|
466
|
+
rawMax: rawMax
|
|
467
|
+
}));
|
|
458
468
|
}).y0(function (d) {
|
|
459
469
|
return yScale(d.opacity);
|
|
460
470
|
}).y1(innerHeight).curve(d3.curveLinear);
|
|
461
471
|
return (_areaGenerator = areaGenerator(controlPointsToRender)) !== null && _areaGenerator !== void 0 ? _areaGenerator : undefined;
|
|
462
|
-
}, [controlPointsToRender, xScale, yScale, innerHeight]);
|
|
472
|
+
}, [controlPointsToRender, xScale, yScale, innerHeight, rawMin, rawMax]);
|
|
463
473
|
|
|
464
474
|
/** d3-generated svg data string representing the "basic mode" min/max slider handles */
|
|
465
475
|
var sliderHandlePath = useMemo(function () {
|
|
@@ -503,11 +513,11 @@ var TfEditor = function TfEditor(props) {
|
|
|
503
513
|
if (el === null) {
|
|
504
514
|
return;
|
|
505
515
|
}
|
|
506
|
-
var numBins =
|
|
516
|
+
var numBins = histogram.getNumBins();
|
|
507
517
|
if (numBins < 1) {
|
|
508
518
|
return;
|
|
509
519
|
}
|
|
510
|
-
var _getHistogramBinLengt = getHistogramBinLengths(
|
|
520
|
+
var _getHistogramBinLengt = getHistogramBinLengths(histogram),
|
|
511
521
|
binLengths = _getHistogramBinLengt.binLengths,
|
|
512
522
|
max = _getHistogramBinLengt.max;
|
|
513
523
|
var start = Math.max(0, Math.ceil(plotMinU8));
|
|
@@ -519,18 +529,21 @@ var TfEditor = function TfEditor(props) {
|
|
|
519
529
|
.data(binLengthsToRender) // bind the histogram bins to this selection
|
|
520
530
|
.join("rect") // ensure we have exactly as many bound `rect` elements in the DOM as we have histogram bins
|
|
521
531
|
.attr("class", "bar").attr("width", barWidth).attr("x", function (_len, idx) {
|
|
522
|
-
return xScale(u8ToAbsolute(idx + start,
|
|
532
|
+
return xScale(u8ToAbsolute(idx + start, {
|
|
533
|
+
rawMin: rawMin,
|
|
534
|
+
rawMax: rawMax
|
|
535
|
+
}));
|
|
523
536
|
}) // set position and height from data
|
|
524
537
|
.attr("y", function (len) {
|
|
525
538
|
return binScale(len);
|
|
526
539
|
}).attr("height", function (len) {
|
|
527
540
|
return innerHeight - binScale(len);
|
|
528
541
|
});
|
|
529
|
-
}, [xScale,
|
|
542
|
+
}, [xScale, rawMin, rawMax, histogram, innerWidth, innerHeight, plotMinU8, plotMaxU8]);
|
|
530
543
|
var applyTFGenerator = useCallback(function (generator) {
|
|
531
544
|
setSelectedPointIdx(null);
|
|
532
545
|
lastColorRef.current = TFEDITOR_DEFAULT_COLOR;
|
|
533
|
-
var lut = TF_GENERATORS[generator](
|
|
546
|
+
var lut = TF_GENERATORS[generator](histogram);
|
|
534
547
|
if (props.useControlPoints) {
|
|
535
548
|
setControlPoints(lut.controlPoints.map(function (cp) {
|
|
536
549
|
return _objectSpread(_objectSpread({}, cp), {}, {
|
|
@@ -540,12 +553,13 @@ var TfEditor = function TfEditor(props) {
|
|
|
540
553
|
} else {
|
|
541
554
|
setRamp(controlPointsToRamp(lut.controlPoints));
|
|
542
555
|
}
|
|
543
|
-
}, [
|
|
556
|
+
}, [histogram, props.useControlPoints, setControlPoints, setRamp]);
|
|
544
557
|
var createTFGeneratorButton = function createTFGeneratorButton(generator, name, description) {
|
|
545
558
|
return /*#__PURE__*/React.createElement(Tooltip, {
|
|
546
559
|
title: description,
|
|
547
560
|
placement: "top"
|
|
548
561
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
562
|
+
size: "small",
|
|
549
563
|
onClick: function onClick() {
|
|
550
564
|
return applyTFGenerator(generator);
|
|
551
565
|
}
|
|
@@ -572,14 +586,14 @@ var TfEditor = function TfEditor(props) {
|
|
|
572
586
|
onContextMenu: handleControlPointContextMenu
|
|
573
587
|
});
|
|
574
588
|
}) : null;
|
|
575
|
-
// move selected control point to the end so it's not occluded by other nearby points
|
|
589
|
+
// move selected control point to the end so it's drawn last and not occluded by other nearby points
|
|
576
590
|
if (controlPointCircles !== null && selectedPointIdx !== null) {
|
|
577
591
|
controlPointCircles.push(controlPointCircles.splice(selectedPointIdx, 1)[0]);
|
|
578
592
|
}
|
|
579
593
|
var viewerModeString = props.useControlPoints ? "advanced" : "basic";
|
|
580
594
|
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
|
|
581
595
|
className: "button-row"
|
|
582
|
-
}, createTFGeneratorButton("
|
|
596
|
+
}, createTFGeneratorButton("auto98XF", "Default", "Ramp from 50th percentile to 98th"), createTFGeneratorButton("auto2XF", "IJ Auto", "Emulates ImageJ's \"auto\" button"), createTFGeneratorButton("resetXF", "Auto 1", "Ramp over the full data range (0% to 100%)"), createTFGeneratorButton("bestFitXF", "Auto 2", "Ramp over the middle 80% of data"), /*#__PURE__*/React.createElement(Checkbox, {
|
|
583
597
|
checked: props.useControlPoints,
|
|
584
598
|
onChange: function onChange(e) {
|
|
585
599
|
return changeChannelSetting({
|
|
@@ -591,7 +605,7 @@ var TfEditor = function TfEditor(props) {
|
|
|
591
605
|
}
|
|
592
606
|
}, "Advanced")), !props.useControlPoints && /*#__PURE__*/React.createElement("div", {
|
|
593
607
|
className: "tf-editor-control-row ramp-row"
|
|
594
|
-
}, "
|
|
608
|
+
}, "Levels min/max", /*#__PURE__*/React.createElement(InputNumber, {
|
|
595
609
|
value: u8ToAbsolute(props.ramp[0], props.channelData),
|
|
596
610
|
onChange: function onChange(v) {
|
|
597
611
|
return v !== null && setRamp([absoluteToU8(v, props.channelData), props.ramp[1]]);
|
|
@@ -691,26 +705,52 @@ var TfEditor = function TfEditor(props) {
|
|
|
691
705
|
}
|
|
692
706
|
}))))), /*#__PURE__*/React.createElement("div", {
|
|
693
707
|
className: "tf-editor-control-row plot-range-row"
|
|
694
|
-
},
|
|
695
|
-
|
|
696
|
-
onChange: function onChange(e) {
|
|
697
|
-
return changeChannelSetting({
|
|
698
|
-
lockPlotToDataRange: e.target.checked
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
}, "Lock to data range")), !props.lockPlotToDataRange && /*#__PURE__*/React.createElement("span", null, "Plot max", " ", /*#__PURE__*/React.createElement(InputNumber, {
|
|
702
|
-
value: props.plotMax,
|
|
708
|
+
}, "Plot min/max", /*#__PURE__*/React.createElement(InputNumber, {
|
|
709
|
+
value: plotMin,
|
|
703
710
|
onChange: function onChange(v) {
|
|
704
711
|
return v !== null && changeChannelSetting({
|
|
705
|
-
|
|
712
|
+
plotMin: v,
|
|
713
|
+
plotMax: Math.max(v + 1, plotMax)
|
|
706
714
|
});
|
|
707
715
|
},
|
|
708
716
|
formatter: numberFormatter,
|
|
709
717
|
min: typeRange.min,
|
|
718
|
+
max: typeRange.max - 1,
|
|
719
|
+
size: "small",
|
|
720
|
+
controls: false
|
|
721
|
+
}), /*#__PURE__*/React.createElement(InputNumber, {
|
|
722
|
+
value: plotMax,
|
|
723
|
+
onChange: function onChange(v) {
|
|
724
|
+
return v !== null && changeChannelSetting({
|
|
725
|
+
plotMax: v,
|
|
726
|
+
plotMin: Math.min(v - 1, plotMin)
|
|
727
|
+
});
|
|
728
|
+
},
|
|
729
|
+
formatter: numberFormatter,
|
|
730
|
+
min: typeRange.min + 1,
|
|
710
731
|
max: typeRange.max,
|
|
711
732
|
size: "small",
|
|
712
733
|
controls: false
|
|
713
|
-
})
|
|
734
|
+
}), /*#__PURE__*/React.createElement(Button, {
|
|
735
|
+
size: "small",
|
|
736
|
+
style: {
|
|
737
|
+
marginLeft: "12px"
|
|
738
|
+
},
|
|
739
|
+
onClick: function onClick() {
|
|
740
|
+
return changeChannelSetting({
|
|
741
|
+
plotMin: rawMin,
|
|
742
|
+
plotMax: rawMax
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}, "Fit to data"), /*#__PURE__*/React.createElement(Button, {
|
|
746
|
+
size: "small",
|
|
747
|
+
onClick: function onClick() {
|
|
748
|
+
return changeChannelSetting({
|
|
749
|
+
plotMin: typeRange.min,
|
|
750
|
+
plotMax: typeRange.max
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}, "Full range")), /*#__PURE__*/React.createElement(SliderRow, {
|
|
714
754
|
label: /*#__PURE__*/React.createElement(Checkbox, {
|
|
715
755
|
checked: props.colorizeEnabled,
|
|
716
756
|
onChange: function onChange(e) {
|
|
@@ -25,21 +25,20 @@
|
|
|
25
25
|
color:var(--color-controlpanel-text);
|
|
26
26
|
align-items:center;
|
|
27
27
|
min-height:24px;
|
|
28
|
+
gap:5px;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
.channel-row .tf-editor-control-row.ramp-row{
|
|
31
|
-
gap:6px;
|
|
32
32
|
margin-top:15px;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
.channel-row .tf-editor-control-row.plot-range-row{
|
|
36
|
-
justify-content:space-between;
|
|
37
36
|
margin-bottom:20px;
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
.channel-row .tf-editor-control-row .ant-input-number{
|
|
41
40
|
border-color:var(--color-button-tertiary-outline);
|
|
42
|
-
width:
|
|
41
|
+
width:64px;
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
.channel-row .tf-editor-control-row .ant-input-number input{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { Radio } from "antd";
|
|
2
|
+
import React from "react";
|
|
3
3
|
import { ViewMode } from "../../shared/enums";
|
|
4
4
|
var viewModes = [ViewMode.threeD, ViewMode.xy, ViewMode.xz, ViewMode.yz];
|
|
5
5
|
var ViewModeRadioButtons = function ViewModeRadioButtons(props) {
|
|
@@ -4,15 +4,15 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
|
|
|
4
4
|
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
5
5
|
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
6
|
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
7
|
-
import {
|
|
7
|
+
import { ReloadOutlined } from "@ant-design/icons";
|
|
8
8
|
import { Button, Radio, Select, Tooltip } from "antd";
|
|
9
9
|
import { debounce } from "lodash";
|
|
10
10
|
import React from "react";
|
|
11
|
-
import ViewModeRadioButtons from "./ViewModeRadioButtons";
|
|
12
|
-
import DownloadButton from "./DownloadButton";
|
|
13
|
-
import { connectToViewerState } from "../ViewerStateProvider";
|
|
14
11
|
import { ImageType, RenderMode, ViewMode } from "../../shared/enums";
|
|
15
12
|
import ViewerIcon from "../shared/ViewerIcon";
|
|
13
|
+
import { connectToViewerState } from "../ViewerStateProvider";
|
|
14
|
+
import DownloadButton from "./DownloadButton";
|
|
15
|
+
import ViewModeRadioButtons from "./ViewModeRadioButtons";
|
|
16
16
|
import "./styles.css";
|
|
17
17
|
var RESIZE_DEBOUNCE_DELAY = 50;
|
|
18
18
|
var visuallyHiddenStyle = {
|
|
@@ -55,34 +55,38 @@ var Toolbar = function Toolbar(props) {
|
|
|
55
55
|
setScrollBtnLeft(barEl.scrollLeft > 0);
|
|
56
56
|
setScrollBtnRight(barEl.scrollLeft < barEl.scrollWidth - barEl.clientWidth);
|
|
57
57
|
}, []);
|
|
58
|
-
var checkSize = React.useCallback(debounce(function () {
|
|
59
|
-
if (!leftRef.current || !centerRef.current || !rightRef.current || !barRef.current) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
var leftRect = leftRef.current.getBoundingClientRect();
|
|
63
|
-
var centerRect = centerRef.current.getBoundingClientRect();
|
|
64
|
-
var rightRect = rightRef.current.getBoundingClientRect();
|
|
65
58
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
var requiredWidth = Math.max(leftRect.width, rightRect.width) * 2 + centerRect.width + SCROLL_OFF_EXTRA_WIDTH;
|
|
73
|
-
if (barWidth > requiredWidth) {
|
|
74
|
-
setScrollMode(false);
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
// Enter scroll mode if centered controls are overlapping either left/right-aligned ones
|
|
78
|
-
if (leftRect.right > centerRect.left || centerRect.right > rightRect.left) {
|
|
79
|
-
setScrollMode(true);
|
|
59
|
+
// This is effectively a `useCallback` - it memoizes a function - but since we're feeding the whole function into
|
|
60
|
+
// lodash's `debounce`, using `useMemo` memoizes the transformation done by `debounce` as well.
|
|
61
|
+
var checkSize = React.useMemo(function () {
|
|
62
|
+
return debounce(function () {
|
|
63
|
+
if (!leftRef.current || !centerRef.current || !rightRef.current || !barRef.current) {
|
|
64
|
+
return;
|
|
80
65
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
66
|
+
var leftRect = leftRef.current.getBoundingClientRect();
|
|
67
|
+
var centerRect = centerRef.current.getBoundingClientRect();
|
|
68
|
+
var rightRect = rightRef.current.getBoundingClientRect();
|
|
69
|
+
|
|
70
|
+
// when calculating width required to leave scroll mode, add a bit of extra width to ensure that triggers
|
|
71
|
+
// for entering and leaving scroll mode never overlap (causing toolbar to rapidly switch when resizing)
|
|
72
|
+
var SCROLL_OFF_EXTRA_WIDTH = 15;
|
|
73
|
+
setScrollMode(function (mode) {
|
|
74
|
+
if (mode) {
|
|
75
|
+
// Leave scroll mode if there is enough space for centered controls not to overlap left/right-aligned ones
|
|
76
|
+
var barWidth = barRef.current.getBoundingClientRect().width;
|
|
77
|
+
var requiredWidth = Math.max(leftRect.width, rightRect.width) * 2 + centerRect.width + SCROLL_OFF_EXTRA_WIDTH;
|
|
78
|
+
return barWidth <= requiredWidth;
|
|
79
|
+
} else {
|
|
80
|
+
// Enter scroll mode if centered controls are overlapping either left/right-aligned ones
|
|
81
|
+
return leftRect.right > centerRect.left || centerRect.right > rightRect.left;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
checkScrollBtnVisible();
|
|
85
|
+
}, RESIZE_DEBOUNCE_DELAY);
|
|
86
|
+
}, [checkScrollBtnVisible]);
|
|
85
87
|
React.useEffect(function () {
|
|
88
|
+
// Make sure `checkSize` is run once on mount
|
|
89
|
+
checkSize();
|
|
86
90
|
window.addEventListener("resize", checkSize);
|
|
87
91
|
return function () {
|
|
88
92
|
return window.removeEventListener("resize", checkSize);
|
|
@@ -157,7 +161,7 @@ var Toolbar = function Toolbar(props) {
|
|
|
157
161
|
}, /*#__PURE__*/React.createElement(Button, {
|
|
158
162
|
className: "ant-btn-icon-only btn-borderless",
|
|
159
163
|
onClick: resetToSavedViewerState
|
|
160
|
-
}, /*#__PURE__*/React.createElement(
|
|
164
|
+
}, /*#__PURE__*/React.createElement(ReloadOutlined, null), /*#__PURE__*/React.createElement("span", {
|
|
161
165
|
style: visuallyHiddenStyle
|
|
162
166
|
}, "Reset to initial settings")))), /*#__PURE__*/React.createElement("div", {
|
|
163
167
|
className: "viewer-toolbar-center",
|
|
@@ -11,6 +11,7 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
|
|
|
11
11
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
12
12
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
13
13
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
14
|
+
import { isEqual } from "lodash";
|
|
14
15
|
import React, { useCallback, useContext, useEffect, useMemo, useReducer, useRef } from "react";
|
|
15
16
|
import { getDefaultViewerChannelSettings, getDefaultViewerState } from "../../shared/constants";
|
|
16
17
|
import { RenderMode, ViewMode } from "../../shared/enums";
|
|
@@ -150,7 +151,7 @@ var ViewerStateProvider = function ViewerStateProvider(props) {
|
|
|
150
151
|
});
|
|
151
152
|
useEffect(function () {
|
|
152
153
|
resetProvider.setSavedViewerState(props.viewerSettings || {});
|
|
153
|
-
}, [props.viewerSettings]);
|
|
154
|
+
}, [props.viewerSettings, resetProvider]);
|
|
154
155
|
var changeViewerSetting = useCallback(function (key, value) {
|
|
155
156
|
return viewerDispatch({
|
|
156
157
|
key: key,
|
|
@@ -178,20 +179,17 @@ var ViewerStateProvider = function ViewerStateProvider(props) {
|
|
|
178
179
|
});
|
|
179
180
|
}, []);
|
|
180
181
|
|
|
181
|
-
// Sync viewer settings prop with state
|
|
182
|
-
// React docs seem to be fine with syncing state with props directly in the render function, but that caused an
|
|
183
|
-
// infinite render loop, so now it's in a `useMemo`:
|
|
184
182
|
// https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
183
|
+
var prevViewerSettingsPropsRef = useRef(undefined);
|
|
184
|
+
if (props.viewerSettings && !isEqual(props.viewerSettings, prevViewerSettingsPropsRef.current)) {
|
|
185
|
+
for (var _i = 0, _arr = Object.keys(props.viewerSettings); _i < _arr.length; _i++) {
|
|
186
|
+
var key = _arr[_i];
|
|
187
|
+
if (viewerSettings[key] !== props.viewerSettings[key]) {
|
|
188
|
+
changeViewerSetting(key, props.viewerSettings[key]);
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
|
-
|
|
191
|
+
prevViewerSettingsPropsRef.current = props.viewerSettings;
|
|
192
|
+
}
|
|
195
193
|
var context = useMemo(function () {
|
|
196
194
|
ref.current = _objectSpread(_objectSpread({}, viewerSettings), {}, {
|
|
197
195
|
channelSettings: channelSettings,
|
|
@@ -214,7 +212,7 @@ var ViewerStateProvider = function ViewerStateProvider(props) {
|
|
|
214
212
|
return {
|
|
215
213
|
ref: ref
|
|
216
214
|
};
|
|
217
|
-
}, [viewerSettings, channelSettings]);
|
|
215
|
+
}, [viewerSettings, channelSettings, applyColorPresets, changeViewerSetting, changeChannelSetting, setChannelSettings, resetProvider]);
|
|
218
216
|
return /*#__PURE__*/React.createElement(ViewerStateContext.Provider, {
|
|
219
217
|
value: context
|
|
220
218
|
}, props.children);
|