@aics/vole-app 2.11.0 → 2.12.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/es/aics-image-viewer/components/App/index.js +19 -5
- package/es/aics-image-viewer/components/AxisClipSliders/index.js +24 -15
- package/es/aics-image-viewer/components/ChannelsWidgetRow/index.js +6 -2
- package/es/aics-image-viewer/components/ErrorAlert/styles.css +3 -0
- package/es/aics-image-viewer/components/StyleProvider/index.js +1 -1
- package/es/aics-image-viewer/components/TfEditor/index.js +184 -36
- package/es/aics-image-viewer/components/TfEditor/styles.css +20 -7
- package/es/aics-image-viewer/shared/constants.js +41 -1
- package/es/aics-image-viewer/shared/utils/viewerState.js +3 -1
- package/package.json +2 -2
- package/type-declarations/aics-image-viewer/components/App/types.d.ts +1 -1
- package/type-declarations/aics-image-viewer/components/TfEditor/index.d.ts +2 -0
- package/type-declarations/aics-image-viewer/components/ViewerStateProvider/types.d.ts +2 -0
- package/type-declarations/aics-image-viewer/shared/constants.d.ts +8 -1
- package/type-declarations/aics-image-viewer/shared/types.d.ts +2 -2
|
@@ -23,7 +23,7 @@ import { Layout } from "antd";
|
|
|
23
23
|
import { debounce, isEqual } from "lodash";
|
|
24
24
|
import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
25
25
|
import { Box3, Vector3 } from "three";
|
|
26
|
-
import { AXIS_MARGIN_DEFAULT, CACHE_MAX_SIZE, CLIPPING_PANEL_HEIGHT_DEFAULT, CLIPPING_PANEL_HEIGHT_TALL, CONTROL_PANEL_CLOSE_WIDTH, getDefaultChannelColor, getDefaultViewerState, QUEUE_MAX_LOW_PRIORITY_SIZE, QUEUE_MAX_SIZE, SCALE_BAR_MARGIN_DEFAULT } from "../../shared/constants";
|
|
26
|
+
import { AXIS_MARGIN_DEFAULT, CACHE_MAX_SIZE, CLIPPING_PANEL_HEIGHT_DEFAULT, CLIPPING_PANEL_HEIGHT_TALL, CONTROL_PANEL_CLOSE_WIDTH, DTYPE_RANGE, getDefaultChannelColor, getDefaultViewerState, QUEUE_MAX_LOW_PRIORITY_SIZE, QUEUE_MAX_SIZE, SCALE_BAR_MARGIN_DEFAULT } from "../../shared/constants";
|
|
27
27
|
import { ImageType, RenderMode, ViewMode } from "../../shared/enums";
|
|
28
28
|
import { activeAxisMap } from "../../shared/types";
|
|
29
29
|
import { colorArrayToFloats } from "../../shared/utils/colorRepresentations";
|
|
@@ -288,7 +288,9 @@ var App = function App(props) {
|
|
|
288
288
|
controlPoints = _initializeLut.controlPoints;
|
|
289
289
|
changeChannelSetting(channelIndex, {
|
|
290
290
|
controlPoints: controlPoints,
|
|
291
|
-
ramp: controlPointsToRamp(ramp)
|
|
291
|
+
ramp: controlPointsToRamp(ramp),
|
|
292
|
+
// set the default range of the transfer function editor to cover the full range of the data type
|
|
293
|
+
plotMax: DTYPE_RANGE[thisChannel.dtype].max
|
|
292
294
|
});
|
|
293
295
|
onResetChannel(channelIndex);
|
|
294
296
|
} else {
|
|
@@ -301,8 +303,8 @@ var App = function App(props) {
|
|
|
301
303
|
var oldRange = channelRangesRef.current[channelIndex];
|
|
302
304
|
if (thisChannelsSettings.useControlPoints) {
|
|
303
305
|
// control points were just automatically remapped - update in state
|
|
304
|
-
// now manually remap ramp using the channel's old range
|
|
305
306
|
var rampControlPoints = rampToControlPoints(thisChannelsSettings.ramp);
|
|
307
|
+
// now manually remap ramp using the channel's old range
|
|
306
308
|
var remappedRampControlPoints = remapControlPointsForChannel(rampControlPoints, oldRange, thisChannel);
|
|
307
309
|
changeChannelSetting(channelIndex, {
|
|
308
310
|
ramp: controlPointsToRamp(remappedRampControlPoints),
|
|
@@ -572,12 +574,24 @@ var App = function App(props) {
|
|
|
572
574
|
if (imageMetadata && metadataFormatter) {
|
|
573
575
|
imageMetadata = metadataFormatter(imageMetadata);
|
|
574
576
|
}
|
|
577
|
+
var sceneMeta;
|
|
578
|
+
if (Array.isArray(metadata)) {
|
|
579
|
+
// If metadata is an array, try to index it by scene
|
|
580
|
+
if (metadata.length >= numScenes) {
|
|
581
|
+
sceneMeta = metadata[viewerState.current.scene];
|
|
582
|
+
} else {
|
|
583
|
+
sceneMeta = metadata[0];
|
|
584
|
+
}
|
|
585
|
+
} else {
|
|
586
|
+
sceneMeta = metadata;
|
|
587
|
+
}
|
|
575
588
|
if (imageMetadata && Object.keys(imageMetadata).length > 0) {
|
|
576
589
|
return _objectSpread({
|
|
577
590
|
Image: imageMetadata
|
|
578
|
-
},
|
|
591
|
+
}, sceneMeta);
|
|
579
592
|
} else {
|
|
580
|
-
|
|
593
|
+
var _sceneMeta;
|
|
594
|
+
return (_sceneMeta = sceneMeta) !== null && _sceneMeta !== void 0 ? _sceneMeta : {};
|
|
581
595
|
}
|
|
582
596
|
}, [props.metadata, props.metadataFormatter, image]);
|
|
583
597
|
|
|
@@ -29,23 +29,30 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
29
29
|
onStart = _ref.onStart,
|
|
30
30
|
onEnd = _ref.onEnd;
|
|
31
31
|
var isRange = vals.length > 1;
|
|
32
|
-
// If slider is a range, handles represent slice *edges*: the range around only the
|
|
33
|
-
//
|
|
34
|
-
|
|
32
|
+
// If slider is a range, handles represent slice *edges*: the range around only the 1st slice is [0, 1]; the range
|
|
33
|
+
// around only the last is [max-1, max].
|
|
34
|
+
// If slider is not a range, just work with slices, but don't 0-index: 1st slice is 1, last is max
|
|
35
|
+
var min = isRange ? 0 : 1;
|
|
36
|
+
var wrappedOnSlide = isRange ? onSlide : function (values) {
|
|
37
|
+
return onSlide === null || onSlide === void 0 ? void 0 : onSlide([values[0] - 1]);
|
|
38
|
+
};
|
|
39
|
+
var wrappedOnChange = isRange ? _onChange : function (values) {
|
|
40
|
+
return _onChange === null || _onChange === void 0 ? void 0 : _onChange([values[0] - 1]);
|
|
41
|
+
};
|
|
35
42
|
return /*#__PURE__*/React.createElement("span", {
|
|
36
43
|
className: "axis-slider-container"
|
|
37
44
|
}, /*#__PURE__*/React.createElement("span", {
|
|
38
45
|
className: "slider-name"
|
|
39
|
-
}, label),
|
|
46
|
+
}, label), max === 0 ? /*#__PURE__*/React.createElement("i", null, "No values to adjust") : /*#__PURE__*/React.createElement("span", {
|
|
40
47
|
className: "axis-slider"
|
|
41
48
|
}, /*#__PURE__*/React.createElement(SmarterSlider, {
|
|
42
49
|
className: isRange ? "" : "slider-single-handle",
|
|
43
50
|
connect: true,
|
|
44
51
|
range: {
|
|
45
|
-
min:
|
|
46
|
-
max:
|
|
52
|
+
min: min,
|
|
53
|
+
max: max
|
|
47
54
|
},
|
|
48
|
-
start: vals,
|
|
55
|
+
start: isRange ? vals : [vals[0] + 1],
|
|
49
56
|
step: 1,
|
|
50
57
|
margin: 1,
|
|
51
58
|
behaviour: "drag",
|
|
@@ -66,25 +73,27 @@ var SliderRow = function SliderRow(_ref) {
|
|
|
66
73
|
to: Math.round,
|
|
67
74
|
from: parseInt
|
|
68
75
|
},
|
|
69
|
-
onSlide:
|
|
70
|
-
onChange:
|
|
76
|
+
onSlide: wrappedOnSlide,
|
|
77
|
+
onChange: wrappedOnChange,
|
|
71
78
|
onStart: onStart,
|
|
72
79
|
onEnd: onEnd
|
|
73
|
-
})),
|
|
80
|
+
})), max > 0 && /*#__PURE__*/React.createElement("span", {
|
|
74
81
|
className: "slider-values"
|
|
75
82
|
}, /*#__PURE__*/React.createElement(NumericInput, {
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
min: min,
|
|
84
|
+
max: max,
|
|
85
|
+
value: valsReadout[0] + (isRange ? 0 : 1),
|
|
78
86
|
onChange: function onChange(value) {
|
|
79
|
-
return _onChange === null || _onChange === void 0 ? void 0 : _onChange(isRange ? [value, vals[1]] : [value]);
|
|
87
|
+
return _onChange === null || _onChange === void 0 ? void 0 : _onChange(isRange ? [value, vals[1]] : [value - 1]);
|
|
80
88
|
}
|
|
81
89
|
}), isRange && /*#__PURE__*/React.createElement(React.Fragment, null, " , ", /*#__PURE__*/React.createElement(NumericInput, {
|
|
82
|
-
|
|
90
|
+
min: min,
|
|
91
|
+
max: max,
|
|
83
92
|
value: valsReadout[1],
|
|
84
93
|
onChange: function onChange(value) {
|
|
85
94
|
return _onChange === null || _onChange === void 0 ? void 0 : _onChange([vals[0], value]);
|
|
86
95
|
}
|
|
87
|
-
})), " / ", max
|
|
96
|
+
})), " / ", max));
|
|
88
97
|
};
|
|
89
98
|
/** Wrapper around `SliderRow` that adds a play button and accounts for the case where not all of an axis is loaded */
|
|
90
99
|
var PlaySliderRow = function PlaySliderRow(props) {
|
|
@@ -93,7 +93,9 @@ var ChannelsWidgetRow = function ChannelsWidgetRow(props) {
|
|
|
93
93
|
colorizeEnabled = channelState.colorizeEnabled,
|
|
94
94
|
colorizeAlpha = channelState.colorizeAlpha,
|
|
95
95
|
useControlPoints = channelState.useControlPoints,
|
|
96
|
-
ramp = channelState.ramp
|
|
96
|
+
ramp = channelState.ramp,
|
|
97
|
+
lockPlotToDataRange = channelState.lockPlotToDataRange,
|
|
98
|
+
plotMax = channelState.plotMax;
|
|
97
99
|
return /*#__PURE__*/React.createElement(TfEditor, {
|
|
98
100
|
id: "TFEditor" + index,
|
|
99
101
|
width: 418,
|
|
@@ -104,7 +106,9 @@ var ChannelsWidgetRow = function ChannelsWidgetRow(props) {
|
|
|
104
106
|
colorizeEnabled: colorizeEnabled,
|
|
105
107
|
colorizeAlpha: colorizeAlpha,
|
|
106
108
|
useControlPoints: useControlPoints,
|
|
107
|
-
ramp: ramp
|
|
109
|
+
ramp: ramp,
|
|
110
|
+
lockPlotToDataRange: lockPlotToDataRange,
|
|
111
|
+
plotMax: plotMax
|
|
108
112
|
});
|
|
109
113
|
};
|
|
110
114
|
var renderSurfaceControls = function renderSurfaceControls() {
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
.load-error-alert .ant-btn-text{
|
|
17
17
|
color:var(--color-text-link);
|
|
18
18
|
}
|
|
19
|
+
.load-error-alert .ant-btn-text:hover{
|
|
20
|
+
color:#25affe !important;
|
|
21
|
+
}
|
|
19
22
|
.load-error-alert .ant-alert-message div:first-child{
|
|
20
23
|
color:var(--color-text-section);
|
|
21
24
|
font-weight:600;
|
|
@@ -134,7 +134,7 @@ var theme = {
|
|
|
134
134
|
}
|
|
135
135
|
};
|
|
136
136
|
/** Makes theme styling available to child components via CSS variables. */
|
|
137
|
-
var CssProvider = styled.div(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n width: 100%;\n height: 100%;\n\n ", "\n\n h1, h2, h3, h4, p {\n font-family: var(--font-family);\n }\n\n h1 {\n color: var(--color-text-header);\n font-size: 28px;\n font-weight: 400;\n }\n\n h2 {\n color: var(--color-text-section);\n font-size: 19px;\n font-weight: 400;\n }\n\n h3 {\n color: var(--color-text-header);\n font-size: 16px;\n font-weight: 600;\n }\n\n h4 {\n color: var(--color-text-header);\n font-size: 14px;\n font-weight: 400;\n }\n\n p {\n color: var(--color-text-body);\n font-size: 14px;\n font-weight: 400;\n }\n\n a {\n color: var(--color-text-link);\n
|
|
137
|
+
var CssProvider = styled.div(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n width: 100%;\n height: 100%;\n\n ", "\n\n h1, h2, h3, h4, p {\n font-family: var(--font-family);\n }\n\n h1 {\n color: var(--color-text-header);\n font-size: 28px;\n font-weight: 400;\n }\n\n h2 {\n color: var(--color-text-section);\n font-size: 19px;\n font-weight: 400;\n }\n\n h3 {\n color: var(--color-text-header);\n font-size: 16px;\n font-weight: 600;\n }\n\n h4 {\n color: var(--color-text-header);\n font-size: 14px;\n font-weight: 400;\n }\n\n p {\n color: var(--color-text-body);\n font-size: 14px;\n font-weight: 400;\n }\n\n a {\n color: var(--color-text-link);\n }\n\n & *::selection {\n background-color: var(--color-text-selection-bg);\n color: var(--color-text-selection-text);\n }\n\n /** TODO: Go through these styles and see if we can use Ant ConfigProvider for them instead. */\n .ant-btn-link,\n .ant-btn-text,\n .ant-btn-primary,\n .ant-btn-icon-only {\n box-shadow: none;\n\n &:hover:not(:disabled),\n &:focus-visible:not(:disabled) {\n background-color: var(--color-button-primary-hover-bg);\n border-color: var(--color-button-primary-hover-bg);\n color: var(--color-button-primary-text);\n }\n\n &:active:not(:disabled) {\n background-color: var(--color-button-primary-hover-bg);\n border: 1px solid var(--color-button-primary-active-outline);\n color: var(--color-button-primary-text);\n }\n }\n\n /* Let us use buttons with type=\"text\" as purely semantic containers - don't bring any styling! */\n .ant-btn-text {\n width: unset;\n height: unset;\n padding: unset;\n\n &:hover:not(:disabled),\n &:focus-visible:not(:disabled) {\n background-color: unset;\n border-color: transparent;\n }\n }\n\n .ant-btn-icon-only:disabled {\n color: var(--color-button-icon-disabled-text);\n }\n\n .ant-btn-link:not(:disabled) {\n // Change from default blue link text\n color: var(--color-button-link-text);\n }\n\n /**\n * Tertiary style buttons. Grey outline by default, turns to light\n * purple outline on hover.\n */\n .ant-btn-default:not(.ant-btn-icon-only) {\n background-color: var(--color-button-tertiary-bg);\n color: var(--color-button-tertiary-text);\n border-color: var(--color-button-tertiary-outline);\n\n &:hover:not(:disabled),\n &:focus-visible:not(:disabled) {\n background-color: transparent;\n border-color: var(--color-button-tertiary-hover-bg);\n color: var(--color-button-tertiary-hover-text);\n }\n\n &:active:not(:disabled) {\n background-color: transparent;\n border-color: var(--color-button-tertiary-active-outline);\n color: var(--color-button-tertiary-active-text);\n }\n }\n\n // Overrides for checkbox styling\n & .ant-checkbox-input {\n &:hover,\n &:focus-visible {\n border: 1px solid white;\n }\n }\n\n & .ant-checkbox.ant-checkbox-checked {\n background-color: var(--color-checkbox-bg);\n }\n\n .ant-checkbox-inner {\n background-color: transparent !important;\n }\n\n // Add outlines to modals and dropdowns\n & .ant-select-dropdown,\n & .ant-dropdown-menu,\n & .ant-modal-content {\n border: 1px solid var(--color-modal-border);\n }\n\n // Force active/hovered text to be white instead of grey for better contrast\n & .ant-dropdown-menu-item-active {\n color: var(--color-menu-hover-text);\n }\n\n // Remove padding in dropdown menus and make items reactangular + flush with the edge\n & .ant-dropdown-menu,\n & .ant-select-dropdown {\n padding: 0;\n overflow: hidden;\n\n & .ant-dropdown-menu-item,\n & .ant-select-item {\n border-radius: 0;\n }\n }\n"])), function (_ref) {
|
|
138
138
|
var $theme = _ref.$theme;
|
|
139
139
|
return css(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n /* Component and color variables. */\n // TODO: Fix inconsistent use of border vs. outline\n // TODO: Remove variables that aren't used in other CSS files. These should be set directly\n // from the $theme object to reduce unnecessary variables.\n --color-text-link: ", ";\n --color-text-header: ", ";\n --color-text-section: ", ";\n --color-text-body: ", ";\n --color-text-error: ", ";\n\n --color-text-selection-bg: ", ";\n --color-text-selection-text: ", ";\n\n --color-header-title: ", ";\n --color-header-hover-title: ", ";\n --color-header-bg: ", ";\n --color-header-border: ", ";\n\n --color-button-primary-bg: ", ";\n --color-button-primary-text: ", ";\n --color-button-primary-hover-bg: ", ";\n --color-button-primary-active-bg: ", ";\n --color-button-primary-active-outline: ", ";\n --color-button-primary-disabled-bg: ", ";\n\n --color-button-link-text: ", ";\n\n --color-button-secondary-bg: ", ";\n --color-button-secondary-text: ", ";\n --color-button-secondary-outline: ", ";\n\n --color-button-tertiary-bg: transparent;\n --color-button-tertiary-text: ", ";\n --color-button-tertiary-outline: ", ";\n --color-button-tertiary-hover-outline: ", ";\n --color-button-tertiary-hover-text: ", ";\n --color-button-tertiary-active-outline: ", ";\n --color-button-tertiary-active-text: ", ";\n\n --color-button-icon-disabled-text: ", ";\n --color-button-icon-disabled-text: ", ";\n --color-button-icon-activated-text: ", ";\n --color-button-icon-activated-bg: ", ";\n --color-button-icon-activated-outline: ", ";\n\n --color-toolbar-button-bg: ", ";\n\n --color-controlpanel-bg: ", ";\n --color-controlpanel-border: ", ";\n --color-controlpanel-section-text: ", ";\n --color-controlpanel-text: ", ";\n --color-controlpanel-section-bg: ", ";\n --color-controlpanel-drawer-bg: ", ";\n --color-controlpanel-ramp-slider: ", ";\n\n --color-landingpage-bg: ", ";\n --color-landingpage-bg-alt: ", ";\n --color-landingpage-text: ", ";\n --color-landingpage-banner-highlight-bg: ", ";\n\n --color-statusflag-border: ", ";\n --color-statusflag-text: ", ";\n\n --color-layout-dividers: ", ";\n\n --color-modal-border: ", ";\n\n --color-menu-hover-text: ", ";\n --color-menu-selected-text: ", ";\n\n --color-checkbox-bg: ", ";\n\n --font-family: ", ";\n "])), $theme.colors.text.link, $theme.colors.text.header, $theme.colors.text.section, $theme.colors.text.body, $theme.colors.text.error, $theme.colors.text.selectionBg, $theme.colors.text.selectionText, $theme.colors.header.title, $theme.colors.header.hoverTitle, $theme.colors.header.bg, $theme.colors.header.border, $theme.colors.button.primary.bg, $theme.colors.button.primary.text, $theme.colors.button.primary.hoverBg, $theme.colors.button.primary.hoverBg, $theme.colors.button.primary.activeOutline, $theme.colors.button.primary.disabledBg, $theme.colors.button.link.text, $theme.colors.button.secondary.bg, $theme.colors.button.secondary.text, $theme.colors.button.secondary.outline, $theme.colors.button.tertiary.text, $theme.colors.button.tertiary.outline, $theme.colors.button.tertiary.hoverOutline, $theme.colors.button.tertiary.hoverText, $theme.colors.button.tertiary.activeOutline, $theme.colors.button.tertiary.hoverText, $theme.colors.button.tertiary.disabledText, $theme.colors.button.tertiary.disabledText, $theme.colors.button.tertiary.activatedText, $theme.colors.button.tertiary.activatedBg, $theme.colors.button.tertiary.activatedOutline, $theme.colors.toolbar.buttonBg, $theme.colors.controlPanel.bg, $theme.colors.controlPanel.border, $theme.colors.controlPanel.sectionText, $theme.colors.controlPanel.text, $theme.colors.controlPanel.sectionBg, $theme.colors.controlPanel.drawerBg, $theme.colors.controlPanel.rampSlider, $theme.colors.landingPage.bg, $theme.colors.landingPage.bgAlt, $theme.colors.landingPage.text, $theme.colors.landingPage.bannerBg, $theme.colors.statusFlag.border, $theme.colors.statusFlag.text, $theme.colors.layout.dividers, $theme.colors.modal.border, $theme.colors.menu.hoverText, $theme.colors.menu.selectedText, $theme.colors.checkbox.bg, $theme.fonts.family);
|
|
140
140
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
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); }
|
|
2
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
2
|
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
5
3
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
6
4
|
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
7
5
|
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
6
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
7
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
8
8
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
9
9
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
10
10
|
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); }
|
|
@@ -20,7 +20,7 @@ import * as d3 from "d3";
|
|
|
20
20
|
import "nouislider/distribute/nouislider.css";
|
|
21
21
|
import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
22
22
|
import { SketchPicker } from "react-color";
|
|
23
|
-
import { LUT_MAX_PERCENTILE, LUT_MIN_PERCENTILE, TFEDITOR_DEFAULT_COLOR } from "../../shared/constants";
|
|
23
|
+
import { DTYPE_RANGE, LUT_MAX_PERCENTILE, LUT_MIN_PERCENTILE, TFEDITOR_DEFAULT_COLOR } from "../../shared/constants";
|
|
24
24
|
import { colorArrayToObject, colorArrayToString, colorObjectToArray } from "../../shared/utils/colorRepresentations";
|
|
25
25
|
import { controlPointsToRamp, rampToControlPoints } from "../../shared/utils/controlPointsToLut";
|
|
26
26
|
import { useRefWithSetter } from "../../shared/utils/hooks";
|
|
@@ -31,8 +31,17 @@ import "./styles.css";
|
|
|
31
31
|
var TFEDITOR_COLOR_PICKER_MARGIN_X_PX = 2;
|
|
32
32
|
/** If a control point is within this distance of the bottom of the screen, open the color picker upward */
|
|
33
33
|
var TFEDITOR_COLOR_PICKER_OPEN_UPWARD_MARGIN_PX = 310;
|
|
34
|
-
var TFEDITOR_GRADIENT_MAX_OPACITY = 0.
|
|
34
|
+
var TFEDITOR_GRADIENT_MAX_OPACITY = 0.75;
|
|
35
35
|
var TFEDITOR_NUM_TICKS = 4;
|
|
36
|
+
/**
|
|
37
|
+
* If the first or last "round" tick mark is within this ratio of the end of the x axis, remove it to get it out of the
|
|
38
|
+
* way of the tick mark right at the end.
|
|
39
|
+
*
|
|
40
|
+
* For instance, if the x range is [0, 255], the last tick mark d3 generates will likely be at 250. That should be
|
|
41
|
+
* removed to get it out of the way of the tick mark at 255! But if the range is [0, 390], it may be that the last tick
|
|
42
|
+
* mark is at 300. It would make no sense to remove that tick mark to make space for one at 390.
|
|
43
|
+
*/
|
|
44
|
+
var TFEDITOR_END_TICK_MARGIN = 0.1;
|
|
36
45
|
var TFEDITOR_MARGINS = {
|
|
37
46
|
top: 18,
|
|
38
47
|
right: 20,
|
|
@@ -112,6 +121,82 @@ function controlPointToAbsolute(cp, channel) {
|
|
|
112
121
|
return u8ToAbsolute(cp.x, channel);
|
|
113
122
|
}
|
|
114
123
|
|
|
124
|
+
/** For when all control points are outside the plot's range: just fill the plot with the settings from 1 point */
|
|
125
|
+
var coverRangeWithPoint = function coverRangeWithPoint(point, plotMin, plotMax) {
|
|
126
|
+
return [_objectSpread(_objectSpread({}, point), {}, {
|
|
127
|
+
x: plotMin
|
|
128
|
+
}), _objectSpread(_objectSpread({}, point), {}, {
|
|
129
|
+
x: plotMax
|
|
130
|
+
})];
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/** For when some control points are outside the plot's range: create an intermediate point on the edge of the range */
|
|
134
|
+
var createPointOnRangeBoundary = function createPointOnRangeBoundary(outOfRangePt, inRangePt, x) {
|
|
135
|
+
var rangeRatio = (x - outOfRangePt.x) / (inRangePt.x - outOfRangePt.x);
|
|
136
|
+
var opacity = outOfRangePt.opacity + (inRangePt.opacity - outOfRangePt.opacity) * rangeRatio;
|
|
137
|
+
var color = outOfRangePt.color.map(function (c, i) {
|
|
138
|
+
return c + (inRangePt.color[i] - c) * rangeRatio;
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
x: x,
|
|
142
|
+
opacity: opacity,
|
|
143
|
+
color: color
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Ensures the list of `controlPoints` exactly covers the range from `plotMin` to `plotMax` by removing any
|
|
149
|
+
* out-of-range points and adding new points right at the edges of the range.
|
|
150
|
+
*/
|
|
151
|
+
var fitControlPointsToRange = function fitControlPointsToRange(controlPoints, plotMin, plotMax) {
|
|
152
|
+
var points = controlPoints.slice();
|
|
153
|
+
var firstPoint = points[0];
|
|
154
|
+
var lastPoint = points[points.length - 1];
|
|
155
|
+
|
|
156
|
+
// If all control points are outside the range, just fill the range with the first or last point.
|
|
157
|
+
if (lastPoint.x < plotMin) {
|
|
158
|
+
return coverRangeWithPoint(lastPoint, plotMin, plotMax);
|
|
159
|
+
}
|
|
160
|
+
if (firstPoint.x > plotMax) {
|
|
161
|
+
return coverRangeWithPoint(firstPoint, plotMin, plotMax);
|
|
162
|
+
}
|
|
163
|
+
if (firstPoint.x > plotMin) {
|
|
164
|
+
// If the control points don't go all the way to the min, add a new point at the min.
|
|
165
|
+
points.unshift(_objectSpread(_objectSpread({}, firstPoint), {}, {
|
|
166
|
+
x: plotMin
|
|
167
|
+
}));
|
|
168
|
+
} else {
|
|
169
|
+
// If some control points are out of range, remove those points...
|
|
170
|
+
var outOfRangePoint = undefined;
|
|
171
|
+
while (points[0].x < plotMin && points.length > 1) {
|
|
172
|
+
outOfRangePoint = points.shift();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ...and create a new point at the edge of the range.
|
|
176
|
+
if (outOfRangePoint !== undefined) {
|
|
177
|
+
points.unshift(createPointOnRangeBoundary(outOfRangePoint, points[0], plotMin));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (lastPoint.x < plotMax) {
|
|
181
|
+
// If the control points don't go all the way to the max, add a new point at the max.
|
|
182
|
+
points.push(_objectSpread(_objectSpread({}, lastPoint), {}, {
|
|
183
|
+
x: plotMax
|
|
184
|
+
}));
|
|
185
|
+
} else {
|
|
186
|
+
// If some control points are out of range, remove those points...
|
|
187
|
+
var _outOfRangePoint = undefined;
|
|
188
|
+
while (points[points.length - 1].x > plotMax && points.length > 1) {
|
|
189
|
+
_outOfRangePoint = points.pop();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ...and create a new point at the edge of the range.
|
|
193
|
+
if (_outOfRangePoint !== undefined) {
|
|
194
|
+
points.push(createPointOnRangeBoundary(_outOfRangePoint, points[points.length - 1], plotMax));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return points;
|
|
198
|
+
};
|
|
199
|
+
|
|
115
200
|
/** Defines an SVG gradient with id `id` based on the provided `controlPoints` */
|
|
116
201
|
var ControlPointGradientDef = function ControlPointGradientDef(_ref) {
|
|
117
202
|
var controlPoints = _ref.controlPoints,
|
|
@@ -206,11 +291,25 @@ var TfEditor = function TfEditor(props) {
|
|
|
206
291
|
var lastColorRef = useRef(TFEDITOR_DEFAULT_COLOR);
|
|
207
292
|
var svgRef = useRef(null); // need access to SVG element to measure mouse position
|
|
208
293
|
|
|
294
|
+
var _props$channelData = props.channelData,
|
|
295
|
+
rawMin = _props$channelData.rawMin,
|
|
296
|
+
rawMax = _props$channelData.rawMax,
|
|
297
|
+
dtype = _props$channelData.dtype;
|
|
298
|
+
var typeRange = DTYPE_RANGE[dtype];
|
|
299
|
+
|
|
209
300
|
// d3 scales define the mapping between data and screen space (and do the heavy lifting of generating plot axes)
|
|
210
301
|
/** `xScale` is in raw intensity range, not U8 range. We use `u8ToAbsolute` and `absoluteToU8` to translate to U8. */
|
|
211
|
-
var
|
|
212
|
-
|
|
213
|
-
|
|
302
|
+
var _useMemo = useMemo(function () {
|
|
303
|
+
var domain = props.lockPlotToDataRange ? [rawMin, rawMax] : [typeRange.min, props.plotMax];
|
|
304
|
+
var scale = d3.scaleLinear().domain(domain).range([0, innerWidth]);
|
|
305
|
+
var plotMinU8 = absoluteToU8(domain[0], props.channelData);
|
|
306
|
+
var plotMaxU8 = absoluteToU8(domain[1], props.channelData);
|
|
307
|
+
return [scale, plotMinU8, plotMaxU8];
|
|
308
|
+
}, [innerWidth, rawMin, rawMax, typeRange, props.lockPlotToDataRange, props.plotMax]),
|
|
309
|
+
_useMemo2 = _slicedToArray(_useMemo, 3),
|
|
310
|
+
xScale = _useMemo2[0],
|
|
311
|
+
plotMinU8 = _useMemo2[1],
|
|
312
|
+
plotMaxU8 = _useMemo2[2];
|
|
214
313
|
var yScale = useMemo(function () {
|
|
215
314
|
return d3.scaleLinear().domain([0, 1]).range([innerHeight, 0]);
|
|
216
315
|
}, [innerHeight]);
|
|
@@ -347,8 +446,9 @@ var TfEditor = function TfEditor(props) {
|
|
|
347
446
|
}
|
|
348
447
|
};
|
|
349
448
|
var controlPointsToRender = useMemo(function () {
|
|
350
|
-
|
|
351
|
-
|
|
449
|
+
var points = props.useControlPoints ? props.controlPoints.slice() : rampToControlPoints(props.ramp);
|
|
450
|
+
return fitControlPointsToRange(points, plotMinU8, plotMaxU8);
|
|
451
|
+
}, [props.controlPoints, props.ramp, props.useControlPoints, plotMinU8, plotMaxU8]);
|
|
352
452
|
|
|
353
453
|
/** d3-generated svg data string representing both the line between points and the region filled with gradient */
|
|
354
454
|
var areaPath = useMemo(function () {
|
|
@@ -372,8 +472,27 @@ var TfEditor = function TfEditor(props) {
|
|
|
372
472
|
// identity changes (i.e. whenever their dependencies change).
|
|
373
473
|
|
|
374
474
|
var xAxisRef = useCallback(function (el) {
|
|
475
|
+
// generate tick marks
|
|
375
476
|
var ticks = xScale.ticks(TFEDITOR_NUM_TICKS);
|
|
376
|
-
|
|
477
|
+
|
|
478
|
+
// make sure we have sensible tick marks right at the min and max of the x axis
|
|
479
|
+
var _xScale$domain = xScale.domain(),
|
|
480
|
+
_xScale$domain2 = _slicedToArray(_xScale$domain, 2),
|
|
481
|
+
min = _xScale$domain2[0],
|
|
482
|
+
max = _xScale$domain2[1];
|
|
483
|
+
var domain = max - min;
|
|
484
|
+
if ((ticks[0] - min) / domain < TFEDITOR_END_TICK_MARGIN) {
|
|
485
|
+
ticks[0] = min;
|
|
486
|
+
} else {
|
|
487
|
+
ticks.unshift(min);
|
|
488
|
+
}
|
|
489
|
+
if ((ticks[ticks.length - 1] - min) / domain > 1 - TFEDITOR_END_TICK_MARGIN) {
|
|
490
|
+
ticks[ticks.length - 1] = max;
|
|
491
|
+
} else {
|
|
492
|
+
ticks.push(max);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// now make the axis!
|
|
377
496
|
d3.select(el).call(d3.axisBottom(xScale).tickValues(ticks).tickPadding(props.useControlPoints ? 3 : 10) // get tick labels out of the way of sliders in "basic" mode
|
|
378
497
|
);
|
|
379
498
|
}, [xScale, props.useControlPoints]);
|
|
@@ -384,26 +503,30 @@ var TfEditor = function TfEditor(props) {
|
|
|
384
503
|
if (el === null) {
|
|
385
504
|
return;
|
|
386
505
|
}
|
|
387
|
-
|
|
506
|
+
var numBins = props.channelData.histogram.getNumBins();
|
|
507
|
+
if (numBins < 1) {
|
|
388
508
|
return;
|
|
389
509
|
}
|
|
390
510
|
var _getHistogramBinLengt = getHistogramBinLengths(props.channelData.histogram),
|
|
391
511
|
binLengths = _getHistogramBinLengt.binLengths,
|
|
392
512
|
max = _getHistogramBinLengt.max;
|
|
393
|
-
var
|
|
513
|
+
var start = Math.max(0, Math.ceil(plotMinU8));
|
|
514
|
+
var end = Math.min(numBins, Math.floor(plotMaxU8));
|
|
515
|
+
var binLengthsToRender = binLengths.slice(start, end);
|
|
516
|
+
var barWidth = innerWidth / (plotMaxU8 - plotMinU8);
|
|
394
517
|
var binScale = d3.scaleLog().domain([0.1, max]).range([innerHeight, 0]).base(2).clamp(true);
|
|
395
518
|
d3.select(el).selectAll(".bar") // select all the bars of the histogram
|
|
396
|
-
.data(
|
|
519
|
+
.data(binLengthsToRender) // bind the histogram bins to this selection
|
|
397
520
|
.join("rect") // ensure we have exactly as many bound `rect` elements in the DOM as we have histogram bins
|
|
398
521
|
.attr("class", "bar").attr("width", barWidth).attr("x", function (_len, idx) {
|
|
399
|
-
return xScale(u8ToAbsolute(idx, props.channelData));
|
|
522
|
+
return xScale(u8ToAbsolute(idx + start, props.channelData));
|
|
400
523
|
}) // set position and height from data
|
|
401
524
|
.attr("y", function (len) {
|
|
402
525
|
return binScale(len);
|
|
403
526
|
}).attr("height", function (len) {
|
|
404
527
|
return innerHeight - binScale(len);
|
|
405
528
|
});
|
|
406
|
-
}, [props.channelData.histogram, innerWidth, innerHeight]);
|
|
529
|
+
}, [xScale, props.channelData, props.channelData.histogram, innerWidth, innerHeight, plotMinU8, plotMaxU8]);
|
|
407
530
|
var applyTFGenerator = useCallback(function (generator) {
|
|
408
531
|
setSelectedPointIdx(null);
|
|
409
532
|
lastColorRef.current = TFEDITOR_DEFAULT_COLOR;
|
|
@@ -430,7 +553,10 @@ var TfEditor = function TfEditor(props) {
|
|
|
430
553
|
};
|
|
431
554
|
|
|
432
555
|
// create one svg circle element for each control point
|
|
433
|
-
var controlPointCircles = props.useControlPoints ? props.controlPoints.
|
|
556
|
+
var controlPointCircles = props.useControlPoints ? props.controlPoints.filter(function (cp) {
|
|
557
|
+
return plotMinU8 <= cp.x && cp.x <= plotMaxU8;
|
|
558
|
+
}) // filter out-of-range points
|
|
559
|
+
.map(function (cp, i) {
|
|
434
560
|
return /*#__PURE__*/React.createElement("circle", {
|
|
435
561
|
key: i,
|
|
436
562
|
className: i === selectedPointIdx ? "selected" : "",
|
|
@@ -463,7 +589,29 @@ var TfEditor = function TfEditor(props) {
|
|
|
463
589
|
style: {
|
|
464
590
|
marginLeft: "auto"
|
|
465
591
|
}
|
|
466
|
-
}, "Advanced")),
|
|
592
|
+
}, "Advanced")), !props.useControlPoints && /*#__PURE__*/React.createElement("div", {
|
|
593
|
+
className: "tf-editor-control-row ramp-row"
|
|
594
|
+
}, "Ramp min/max", /*#__PURE__*/React.createElement(InputNumber, {
|
|
595
|
+
value: u8ToAbsolute(props.ramp[0], props.channelData),
|
|
596
|
+
onChange: function onChange(v) {
|
|
597
|
+
return v !== null && setRamp([absoluteToU8(v, props.channelData), props.ramp[1]]);
|
|
598
|
+
},
|
|
599
|
+
formatter: numberFormatter,
|
|
600
|
+
min: typeRange.min,
|
|
601
|
+
max: Math.min(u8ToAbsolute(props.ramp[1], props.channelData), typeRange.max),
|
|
602
|
+
size: "small",
|
|
603
|
+
controls: false
|
|
604
|
+
}), /*#__PURE__*/React.createElement(InputNumber, {
|
|
605
|
+
value: u8ToAbsolute(props.ramp[1], props.channelData),
|
|
606
|
+
onChange: function onChange(v) {
|
|
607
|
+
return v !== null && setRamp([props.ramp[0], absoluteToU8(v, props.channelData)]);
|
|
608
|
+
},
|
|
609
|
+
formatter: numberFormatter,
|
|
610
|
+
min: Math.max(typeRange.min, u8ToAbsolute(props.ramp[0], props.channelData)),
|
|
611
|
+
max: typeRange.max,
|
|
612
|
+
size: "small",
|
|
613
|
+
controls: false
|
|
614
|
+
})), colorPickerPosition !== null && /*#__PURE__*/React.createElement("div", {
|
|
467
615
|
className: "tf-editor-popover"
|
|
468
616
|
}, /*#__PURE__*/React.createElement("div", {
|
|
469
617
|
className: "tf-editor-cover",
|
|
@@ -504,7 +652,7 @@ var TfEditor = function TfEditor(props) {
|
|
|
504
652
|
className: "axis"
|
|
505
653
|
}), controlPointCircles, !props.useControlPoints && /*#__PURE__*/React.createElement("g", {
|
|
506
654
|
className: "ramp-sliders"
|
|
507
|
-
}, /*#__PURE__*/React.createElement("g", {
|
|
655
|
+
}, plotMinU8 <= props.ramp[0] && props.ramp[0] <= plotMaxU8 && /*#__PURE__*/React.createElement("g", {
|
|
508
656
|
transform: "translate(".concat(xScale(u8ToAbsolute(props.ramp[0], props.channelData)), ")")
|
|
509
657
|
}, /*#__PURE__*/React.createElement("line", {
|
|
510
658
|
y1: innerHeight,
|
|
@@ -523,7 +671,7 @@ var TfEditor = function TfEditor(props) {
|
|
|
523
671
|
onPointerDown: function onPointerDown() {
|
|
524
672
|
return setDraggedPointIdx(TfEditorRampSliderHandle.Min);
|
|
525
673
|
}
|
|
526
|
-
})), /*#__PURE__*/React.createElement("g", {
|
|
674
|
+
})), plotMinU8 <= props.ramp[1] && props.ramp[1] <= plotMaxU8 && /*#__PURE__*/React.createElement("g", {
|
|
527
675
|
transform: "translate(".concat(xScale(u8ToAbsolute(props.ramp[1], props.channelData)), ")")
|
|
528
676
|
}, /*#__PURE__*/React.createElement("line", {
|
|
529
677
|
y1: innerHeight,
|
|
@@ -541,27 +689,27 @@ var TfEditor = function TfEditor(props) {
|
|
|
541
689
|
onPointerDown: function onPointerDown() {
|
|
542
690
|
return setDraggedPointIdx(TfEditorRampSliderHandle.Max);
|
|
543
691
|
}
|
|
544
|
-
}))))),
|
|
545
|
-
className: "tf-editor-
|
|
546
|
-
}, /*#__PURE__*/React.createElement("span", null,
|
|
547
|
-
|
|
548
|
-
onChange: function onChange(
|
|
549
|
-
return
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
})), /*#__PURE__*/React.createElement("span", null, "Max", " ", /*#__PURE__*/React.createElement(InputNumber, {
|
|
556
|
-
value: u8ToAbsolute(props.ramp[1], props.channelData),
|
|
692
|
+
}))))), /*#__PURE__*/React.createElement("div", {
|
|
693
|
+
className: "tf-editor-control-row plot-range-row"
|
|
694
|
+
}, /*#__PURE__*/React.createElement("span", null, /*#__PURE__*/React.createElement(Checkbox, {
|
|
695
|
+
checked: props.lockPlotToDataRange,
|
|
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,
|
|
557
703
|
onChange: function onChange(v) {
|
|
558
|
-
return v !== null &&
|
|
704
|
+
return v !== null && changeChannelSetting({
|
|
705
|
+
plotMax: v
|
|
706
|
+
});
|
|
559
707
|
},
|
|
560
708
|
formatter: numberFormatter,
|
|
561
|
-
min:
|
|
562
|
-
max:
|
|
709
|
+
min: typeRange.min,
|
|
710
|
+
max: typeRange.max,
|
|
563
711
|
size: "small",
|
|
564
|
-
|
|
712
|
+
controls: false
|
|
565
713
|
}))), /*#__PURE__*/React.createElement(SliderRow, {
|
|
566
714
|
label: /*#__PURE__*/React.createElement(Checkbox, {
|
|
567
715
|
checked: props.colorizeEnabled,
|
|
@@ -20,19 +20,32 @@
|
|
|
20
20
|
bottom:0;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
.channel-row .tf-editor-
|
|
23
|
+
.channel-row .tf-editor-control-row{
|
|
24
24
|
display:flex;
|
|
25
|
-
align-items:center;
|
|
26
|
-
justify-content:space-between;
|
|
27
|
-
margin-bottom:20px;
|
|
28
25
|
color:var(--color-controlpanel-text);
|
|
26
|
+
align-items:center;
|
|
27
|
+
min-height:24px;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
.channel-row .tf-editor-
|
|
30
|
+
.channel-row .tf-editor-control-row.ramp-row{
|
|
31
|
+
gap:6px;
|
|
32
|
+
margin-top:15px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.channel-row .tf-editor-control-row.plot-range-row{
|
|
36
|
+
justify-content:space-between;
|
|
37
|
+
margin-bottom:20px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.channel-row .tf-editor-control-row .ant-input-number{
|
|
32
41
|
border-color:var(--color-button-tertiary-outline);
|
|
33
|
-
width:
|
|
42
|
+
width:72px;
|
|
34
43
|
}
|
|
35
44
|
|
|
45
|
+
.channel-row .tf-editor-control-row .ant-input-number input{
|
|
46
|
+
text-align:right;
|
|
47
|
+
}
|
|
48
|
+
|
|
36
49
|
.channel-row .tick text{
|
|
37
50
|
fill:var(--color-controlpanel-text);
|
|
38
51
|
user-select:none;
|
|
@@ -58,7 +71,7 @@
|
|
|
58
71
|
|
|
59
72
|
.channel-row .line{
|
|
60
73
|
stroke:#aeacae;
|
|
61
|
-
stroke-width:
|
|
74
|
+
stroke-width:1px;
|
|
62
75
|
}
|
|
63
76
|
|
|
64
77
|
.channel-row .ramp-sliders path{
|
|
@@ -31,6 +31,44 @@ export var TFEDITOR_MAX_BIN = 255;
|
|
|
31
31
|
export var CACHE_MAX_SIZE = 1000000000;
|
|
32
32
|
export var QUEUE_MAX_SIZE = 10;
|
|
33
33
|
export var QUEUE_MAX_LOW_PRIORITY_SIZE = 4;
|
|
34
|
+
|
|
35
|
+
/** Maps channel data types to their minimum and maximum values */
|
|
36
|
+
export var DTYPE_RANGE = {
|
|
37
|
+
int8: {
|
|
38
|
+
min: -Math.pow(2, 7),
|
|
39
|
+
max: Math.pow(2, 7) - 1
|
|
40
|
+
},
|
|
41
|
+
int16: {
|
|
42
|
+
min: -Math.pow(2, 15),
|
|
43
|
+
max: Math.pow(2, 15) - 1
|
|
44
|
+
},
|
|
45
|
+
int32: {
|
|
46
|
+
min: -Math.pow(2, 31),
|
|
47
|
+
max: Math.pow(2, 31) - 1
|
|
48
|
+
},
|
|
49
|
+
uint8: {
|
|
50
|
+
min: 0,
|
|
51
|
+
max: Math.pow(2, 8) - 1
|
|
52
|
+
},
|
|
53
|
+
uint16: {
|
|
54
|
+
min: 0,
|
|
55
|
+
max: Math.pow(2, 16) - 1
|
|
56
|
+
},
|
|
57
|
+
uint32: {
|
|
58
|
+
min: 0,
|
|
59
|
+
max: Math.pow(2, 32) - 1
|
|
60
|
+
},
|
|
61
|
+
// These are obviously not the actual min and max representable values for floats, but the actual ones (`-Infinity`
|
|
62
|
+
// and `Infinity`) would give us nonsense. These may also produce nonsense, but these types should be rare anyways.
|
|
63
|
+
float32: {
|
|
64
|
+
min: 0,
|
|
65
|
+
max: Math.pow(2, 8) - 1
|
|
66
|
+
},
|
|
67
|
+
float64: {
|
|
68
|
+
min: 0,
|
|
69
|
+
max: Math.pow(2, 8) - 1
|
|
70
|
+
}
|
|
71
|
+
};
|
|
34
72
|
export var PRESET_COLORS_1 = [[190, 68, 171], [189, 211, 75], [61, 155, 169], [128, 128, 128], [255, 255, 255], [239, 27, 45], [238, 77, 245], [96, 255, 255]];
|
|
35
73
|
export var PRESET_COLORS_2 = [[128, 0, 0], [0, 128, 0], [0, 0, 128], [32, 32, 32], [255, 255, 0], [255, 0, 255], [0, 255, 0], [0, 0, 255]];
|
|
36
74
|
export var PRESET_COLORS_3 = [[128, 0, 128], [128, 128, 128], [0, 128, 128], [128, 128, 0], [255, 255, 255], [255, 0, 0], [255, 0, 255], [0, 255, 255]];
|
|
@@ -159,6 +197,8 @@ export var getDefaultChannelState = function getDefaultChannelState() {
|
|
|
159
197
|
x: 255,
|
|
160
198
|
opacity: 1,
|
|
161
199
|
color: [255, 255, 255]
|
|
162
|
-
}]
|
|
200
|
+
}],
|
|
201
|
+
lockPlotToDataRange: true,
|
|
202
|
+
plotMax: TFEDITOR_MAX_BIN
|
|
163
203
|
};
|
|
164
204
|
};
|
|
@@ -64,6 +64,8 @@ export function initializeOneChannelSetting(channelName, index, defaultColor, vi
|
|
|
64
64
|
color: (_colorHexToArray = colorHexToArray((_initSettings$color = initSettings.color) !== null && _initSettings$color !== void 0 ? _initSettings$color : "")) !== null && _colorHexToArray !== void 0 ? _colorHexToArray : defaultColor,
|
|
65
65
|
useControlPoints: (_initSettings$control = initSettings.controlPointsEnabled) !== null && _initSettings$control !== void 0 ? _initSettings$control : defaultChannelState.useControlPoints,
|
|
66
66
|
controlPoints: (_initSettings$control2 = initSettings.controlPoints) !== null && _initSettings$control2 !== void 0 ? _initSettings$control2 : defaultChannelState.controlPoints,
|
|
67
|
-
ramp: (_initSettings$ramp = initSettings.ramp) !== null && _initSettings$ramp !== void 0 ? _initSettings$ramp : defaultChannelState.ramp
|
|
67
|
+
ramp: (_initSettings$ramp = initSettings.ramp) !== null && _initSettings$ramp !== void 0 ? _initSettings$ramp : defaultChannelState.ramp,
|
|
68
|
+
lockPlotToDataRange: defaultChannelState.lockPlotToDataRange,
|
|
69
|
+
plotMax: defaultChannelState.plotMax
|
|
68
70
|
};
|
|
69
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aics/vole-app",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "web view of cell volume images",
|
|
5
5
|
"repository": "github:allen-cell-animated/vole-app",
|
|
6
6
|
"main": "es/index.js",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"author": "Megan Riel-Mehan",
|
|
42
42
|
"license": "ISC",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@aics/vole-core": "^3.
|
|
44
|
+
"@aics/vole-core": "^3.15.2",
|
|
45
45
|
"@ant-design/icons": "^5.2.5",
|
|
46
46
|
"@fortawesome/fontawesome-svg-core": "^6.5.2",
|
|
47
47
|
"@fortawesome/free-solid-svg-icons": "^6.5.2",
|
|
@@ -37,7 +37,7 @@ export interface AppProps {
|
|
|
37
37
|
translation: [number, number, number];
|
|
38
38
|
rotation: [number, number, number];
|
|
39
39
|
};
|
|
40
|
-
metadata?: MetadataRecord;
|
|
40
|
+
metadata?: MetadataRecord | MetadataRecord[];
|
|
41
41
|
view3dRef?: MutableRefObject<View3d | null>;
|
|
42
42
|
metadataFormatter?: (metadata: MetadataRecord) => MetadataRecord;
|
|
43
43
|
onControlPanelToggle?: (collapsed: boolean) => void;
|
|
@@ -58,6 +58,8 @@ export interface ChannelState {
|
|
|
58
58
|
ramp: [number, number];
|
|
59
59
|
useControlPoints: boolean;
|
|
60
60
|
controlPoints: ControlPoint[];
|
|
61
|
+
lockPlotToDataRange: boolean;
|
|
62
|
+
plotMax: number;
|
|
61
63
|
}
|
|
62
64
|
export type ChannelStateKey = keyof ChannelState;
|
|
63
65
|
export type ChannelSettingUpdater = <K extends ChannelStateKey>(index: number | number[], value: Partial<Record<K, ChannelState[K]>>) => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CameraState } from "@aics/vole-core";
|
|
1
|
+
import type { CameraState, Channel } from "@aics/vole-core";
|
|
2
2
|
import { ChannelState, ViewerState } from "../components/ViewerStateProvider/types";
|
|
3
3
|
import { ViewMode } from "./enums";
|
|
4
4
|
import { ColorArray } from "./utils/colorRepresentations";
|
|
@@ -10,6 +10,13 @@ export declare const TFEDITOR_MAX_BIN = 255;
|
|
|
10
10
|
export declare const CACHE_MAX_SIZE = 1000000000;
|
|
11
11
|
export declare const QUEUE_MAX_SIZE = 10;
|
|
12
12
|
export declare const QUEUE_MAX_LOW_PRIORITY_SIZE = 4;
|
|
13
|
+
/** Maps channel data types to their minimum and maximum values */
|
|
14
|
+
export declare const DTYPE_RANGE: {
|
|
15
|
+
[T in Channel["dtype"]]: {
|
|
16
|
+
min: number;
|
|
17
|
+
max: number;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
13
20
|
export declare const PRESET_COLORS_1: ColorArray[];
|
|
14
21
|
export declare const PRESET_COLORS_2: ColorArray[];
|
|
15
22
|
export declare const PRESET_COLORS_3: ColorArray[];
|
|
@@ -9,7 +9,7 @@ export type IsosurfaceFormat = "GLTF" | "STL";
|
|
|
9
9
|
export type Styles = {
|
|
10
10
|
[key: string]: CSSProperties;
|
|
11
11
|
};
|
|
12
|
-
export type MetadataEntry = string | number | boolean | MetadataRecord | null | undefined;
|
|
12
|
+
export type MetadataEntry = string | number | boolean | MetadataRecord | MetadataEntry[] | null | undefined;
|
|
13
13
|
export type MetadataRecord = {
|
|
14
14
|
[key: string]: MetadataEntry;
|
|
15
|
-
}
|
|
15
|
+
};
|