@aics/vole-app 3.2.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -113,6 +113,7 @@ var App = function App(props) {
113
113
  var time = useViewerState(select("time"));
114
114
  var showAxes = useViewerState(select("showAxes"));
115
115
  var channelSettings = useViewerState(select("channelSettings"));
116
+ var changeViewerSetting = useViewerState(select("changeViewerSetting"));
116
117
  var changeChannelSetting = useViewerState(select("changeChannelSetting"));
117
118
  var applyColorPresets = useViewerState(select("applyColorPresets"));
118
119
  var resetToSavedState = useViewerState(select("resetToSavedViewerState"));
@@ -264,10 +265,12 @@ var App = function App(props) {
264
265
  _useState2 = _slicedToArray(_useState, 2),
265
266
  loadedScene = _useState2[0],
266
267
  setLoadedScene = _useState2[1];
267
- var onChangeScene = useCallback(function (image, sceneIndex) {
268
+ var onChangeScene = useCallback(function (image, sceneIndex, loadSpec) {
269
+ changeViewerSetting("time", loadSpec.time);
268
270
  onImageTitleChange === null || onImageTitleChange === void 0 || onImageTitleChange(image.imageInfo.imageInfo.name);
269
271
  setLoadedScene(sceneIndex);
270
- }, [onImageTitleChange]);
272
+ changeViewerSetting("scaleLevelIndex", image.imageInfo.multiscaleLevel);
273
+ }, [onImageTitleChange, changeViewerSetting]);
271
274
  var onChannelLoaded = useCallback(function (image, channelIndex, isInitialLoad) {
272
275
  // TODO this was once a search by name - is that still necessary or will
273
276
  // the index always be correct?
@@ -585,7 +588,9 @@ var App = function App(props) {
585
588
  resetCamera: resetCamera,
586
589
  downloadScreenshot: saveScreenshot,
587
590
  resetToSavedViewerState: resetToSavedViewerState,
588
- visibleControls: visibleControls
591
+ visibleControls: visibleControls,
592
+ multiscaleDims: image === null || image === void 0 ? void 0 : image.imageInfo.imageInfo.multiscaleLevelDims,
593
+ multiscaleIndex: image === null || image === void 0 ? void 0 : image.imageInfo.multiscaleLevel
589
594
  }), /*#__PURE__*/React.createElement(CellViewerCanvasWrapper, {
590
595
  view3d: view3d,
591
596
  image: image,
@@ -287,7 +287,8 @@ var AxisClipSliders = function AxisClipSliders(props) {
287
287
  onChange: function onChange(_ref6) {
288
288
  var _ref7 = _slicedToArray(_ref6, 1),
289
289
  scene = _ref7[0];
290
- return props.changeViewerSetting("scene", scene);
290
+ props.changeViewerSetting("scene", scene);
291
+ props.changeViewerSetting("useExactScaleLevel", false);
291
292
  }
292
293
  })))));
293
294
  };
@@ -17,6 +17,7 @@ import { RightOutlined } from "@ant-design/icons";
17
17
  import { Alert, Button } from "antd";
18
18
  import React from "react";
19
19
  import { useConstructor } from "../../shared/utils/hooks";
20
+ import { useViewerState } from "../../state/store";
20
21
  import "./styles.css";
21
22
  var IssueLink = function IssueLink(_ref) {
22
23
  var bug = _ref.bug,
@@ -32,7 +33,7 @@ var UNKNOWN_ERROR_DESCRIPTION = /*#__PURE__*/React.createElement(React.Fragment,
32
33
  }, "send us a bug report here"), ".");
33
34
  var ERROR_TYPE_DESCRIPTIONS = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, VolumeLoadErrorType.UNKNOWN, /*#__PURE__*/React.createElement(React.Fragment, null, "An unknown error occurred while loading volume data. Check the browser console (F12) for more details. If this looks like a bug, ", /*#__PURE__*/React.createElement(IssueLink, {
34
35
  bug: true
35
- }, "send us a bug report here"), ".")), VolumeLoadErrorType.NOT_FOUND, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was unable to find any volume data at the specified location. Check that the provided URL is correct and try again.")), VolumeLoadErrorType.TOO_LARGE, /*#__PURE__*/React.createElement(React.Fragment, null, "No scale level is available for this volume which fits within our maximum GPU memory footprint. This maximum is tuned to ensure compatibility with the majority of browsers. If you're trying to load your own OME-Zarr dataset, you may be able to open this volume by including a lower scale level.")), VolumeLoadErrorType.LOAD_DATA_FAILED, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was able to find a source of volume data at the specified location, but encountered an error while trying to load it. Check that your dataset is complete and properly formatted. You can also check the browser console (F12) for more details about this error. If it looks like a problem on our end,", " ", /*#__PURE__*/React.createElement(IssueLink, {
36
+ }, "send us a bug report here"), ".")), VolumeLoadErrorType.NOT_FOUND, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was unable to find any volume data at the specified location. Check that the provided URL is correct and try again.")), VolumeLoadErrorType.TOO_LARGE, /*#__PURE__*/React.createElement(React.Fragment, null, "No resolution level is available for this volume which fits within our maximum memory footprint. This maximum is tuned to ensure compatibility with the majority of browsers. If you're trying to load your own OME-Zarr dataset, you may be able to open this volume by including a lower resolution level.")), VolumeLoadErrorType.LOAD_DATA_FAILED, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was able to find a source of volume data at the specified location, but encountered an error while trying to load it. Check that your dataset is complete and properly formatted. You can also check the browser console (F12) for more details about this error. If it looks like a problem on our end,", " ", /*#__PURE__*/React.createElement(IssueLink, {
36
37
  bug: true
37
38
  }, "start a bug report here"), ".")), VolumeLoadErrorType.INVALID_METADATA, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer was unable to read all necessary information from this volume's metadata. Check that your dataset's metadata is complete and properly formatted. If you believe your data is valid and should be supported by our viewer, let us know by ", /*#__PURE__*/React.createElement(IssueLink, null, "opening a GitHub issue"), ".")), VolumeLoadErrorType.INVALID_MULTI_SOURCE_ZARR, /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer is currently configured to consolidate multiple OME-Zarr datasets into a single volume, but the provided datasets can't all be matched up. Ensure that all dataset URLs are correct and that at least one equivalently-sized scale level exists in all datasets."));
38
39
  var getErrorTitle = function getErrorTitle(error) {
@@ -45,6 +46,9 @@ var getErrorDescription = function getErrorDescription(error) {
45
46
  if (!type) {
46
47
  return _typeof(error) === "object" && error !== null && error.description || UNKNOWN_ERROR_DESCRIPTION;
47
48
  }
49
+ if (type === VolumeLoadErrorType.TOO_LARGE && useViewerState.getState().useExactScaleLevel) {
50
+ return /*#__PURE__*/React.createElement(React.Fragment, null, "The viewer cannot load this resolution level within memory limits. Try again with a smaller resolution level.");
51
+ }
48
52
  return (_ERROR_TYPE_DESCRIPTI2 = ERROR_TYPE_DESCRIPTIONS[type]) !== null && _ERROR_TYPE_DESCRIPTI2 !== void 0 ? _ERROR_TYPE_DESCRIPTI2 : UNKNOWN_ERROR_DESCRIPTION;
49
53
  };
50
54
  var ErrorAlert = function ErrorAlert(_ref2) {
@@ -62,16 +66,22 @@ var ErrorAlert = function ErrorAlert(_ref2) {
62
66
  errorsSeenCount = _React$useState4[0],
63
67
  setErrorsSeenCount = _React$useState4[1];
64
68
  var error = Array.isArray(errors) ? errors[0] : errors;
65
- var errorMessage = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", null, getErrorTitle(error) + (firstErrorCount > 1 ? " (".concat(firstErrorCount, ")") : ""), " ", /*#__PURE__*/React.createElement(Button, {
69
+ var infoStyle = {
70
+ display: showDetails ? undefined : "none"
71
+ };
72
+ var errorMessage = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
73
+ className: "error-title"
74
+ }, getErrorTitle(error) + (firstErrorCount > 1 ? " (".concat(firstErrorCount, ")") : ""), " ", /*#__PURE__*/React.createElement(Button, {
66
75
  type: "text",
67
76
  onClick: function onClick() {
68
77
  return setShowDetails(!showDetails);
69
78
  }
70
79
  }, showDetails ? "Show less info" : "Show more info")), /*#__PURE__*/React.createElement("div", {
71
- style: {
72
- display: showDetails ? undefined : "none"
73
- }
74
- }, getErrorDescription(error)));
80
+ style: infoStyle
81
+ }, getErrorDescription(error)), error.cause !== undefined && /*#__PURE__*/React.createElement("div", {
82
+ className: "error-cause",
83
+ style: infoStyle
84
+ }, "Caused by ", getErrorTitle(error.cause)));
75
85
  var skipErrorButton = Array.isArray(errors) && errors.length > 1 && /*#__PURE__*/React.createElement(Button, {
76
86
  type: "text",
77
87
  onClick: function onClick() {
@@ -7,6 +7,7 @@
7
7
  z-index:5000;
8
8
  border-color:#dc4446;
9
9
  padding:12px 30px;
10
+ color:var(--color-text-section);
10
11
  }
11
12
  .load-error-alert .ant-alert-icon,
12
13
  .load-error-alert .ant-alert-close-icon,
@@ -19,7 +20,10 @@
19
20
  .load-error-alert .ant-btn-text:hover{
20
21
  color:#25affe !important;
21
22
  }
22
- .load-error-alert .ant-alert-message div:first-child{
23
- color:var(--color-text-section);
24
- font-weight:600;
25
- }
23
+ .load-error-alert .ant-alert-message > div.error-title{
24
+ font-weight:600;
25
+ }
26
+ .load-error-alert .ant-alert-message > div.error-cause{
27
+ font-style:italic;
28
+ color:var(--color-text-body);
29
+ }
@@ -19,9 +19,13 @@ var palette = {
19
19
  veryDarkPurple: "#5f369f",
20
20
  veryLtPurple: "#e7e4f2",
21
21
  brightRed: "#ff4d4d",
22
+ brightYellow: "#e39b0d",
22
23
  brightGreen: "#61d900",
23
24
  veryBrightGreen: "#61ff00",
24
- brightBlue: "#0099ff"
25
+ brightBlue: "#0099ff",
26
+ veryBrightBlue: "#0dc4d9",
27
+ darkBlue: "#29484c",
28
+ darkYellow: "#4c3c29"
25
29
  };
26
30
  var theme = {
27
31
  colors: {
@@ -31,7 +35,7 @@ var theme = {
31
35
  primaryDk: palette.darkPurple,
32
36
  success: palette.brightGreen,
33
37
  error: palette.brightRed,
34
- warning: palette.brightRed,
38
+ warning: palette.brightYellow,
35
39
  info: palette.brightBlue
36
40
  },
37
41
  text: {
@@ -121,6 +125,16 @@ var theme = {
121
125
  selectedBg: palette.medGrey,
122
126
  textPlaceholder: palette.ltPurple
123
127
  },
128
+ alert: {
129
+ warning: {
130
+ text: palette.brightYellow,
131
+ bg: palette.darkYellow
132
+ },
133
+ info: {
134
+ text: palette.veryBrightBlue,
135
+ bg: palette.darkBlue
136
+ }
137
+ },
124
138
  tooltip: {
125
139
  bg: palette.black
126
140
  },
@@ -138,7 +152,7 @@ var theme = {
138
152
  /** Makes theme styling available to child components via CSS variables. */
139
153
  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) {
140
154
  var $theme = _ref.$theme;
141
- 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 --color-controlpanel-isovalue-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.controlPanel.isovalueSlider, $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);
155
+ 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-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 --color-controlpanel-isovalue-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-alert-warning-text: ", ";\n --color-alert-warning-bg: ", ";\n --color-alert-info-text: ", ";\n --color-alert-info-bg: ", ";\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.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.controlPanel.isovalueSlider, $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.alert.warning.text, $theme.colors.alert.warning.bg, $theme.colors.alert.info.text, $theme.colors.alert.info.bg, $theme.colors.layout.dividers, $theme.colors.modal.border, $theme.colors.menu.hoverText, $theme.colors.menu.selectedText, $theme.colors.checkbox.bg, $theme.fonts.family);
142
156
  });
143
157
 
144
158
  /**
@@ -157,6 +171,14 @@ export default function StyleProvider(props) {
157
171
  colorBgContainer: "transparent",
158
172
  colorSplit: theme.colors.layout.split,
159
173
  colorPrimaryTextHover: theme.colors.text.selectionText,
174
+ colorWarning: theme.colors.alert.warning.text,
175
+ colorWarningBg: theme.colors.alert.warning.bg,
176
+ colorWarningBorder: theme.colors.alert.warning.text,
177
+ colorWarningText: theme.colors.alert.warning.text,
178
+ colorInfo: theme.colors.alert.info.text,
179
+ colorInfoBg: theme.colors.alert.info.bg,
180
+ colorInfoBorder: theme.colors.alert.info.text,
181
+ colorInfoText: theme.colors.alert.info.text,
160
182
  fontWeightStrong: 400,
161
183
  colorBgElevated: palette.darkGrey,
162
184
  controlItemBgHover: theme.colors.menu.hoverBg,
@@ -172,10 +194,6 @@ export default function StyleProvider(props) {
172
194
  defaultActiveBg: theme.colors.button.secondary.bg,
173
195
  defaultActiveBorderColor: theme.colors.button.tertiary.activeOutline
174
196
  },
175
- Card: {
176
- borderRadiusLG: 0,
177
- headerHeight: 48
178
- },
179
197
  Collapse: {
180
198
  borderRadiusLG: 0,
181
199
  colorTextHeading: theme.colors.text.section
@@ -4,6 +4,8 @@ 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
+ // TODO this really ought to be exported at the top level...
8
+
7
9
  import { ReloadOutlined } from "@ant-design/icons";
8
10
  import { Button, Radio, Select, Tooltip } from "antd";
9
11
  import { debounce } from "lodash";
@@ -27,6 +29,7 @@ var visuallyHiddenStyle = {
27
29
  borderWidth: "0"
28
30
  };
29
31
  var Toolbar = function Toolbar(props) {
32
+ var _props$multiscaleInde;
30
33
  var containerRef = React.useRef(null);
31
34
  var barRef = React.useRef(null);
32
35
  var leftRef = React.useRef(null);
@@ -50,6 +53,8 @@ var Toolbar = function Toolbar(props) {
50
53
  var autorotate = useViewerState(select("autorotate"));
51
54
  var showAxes = useViewerState(select("showAxes"));
52
55
  var showBoundingBox = useViewerState(select("showBoundingBox"));
56
+ var useExactScaleLevel = useViewerState(select("useExactScaleLevel"));
57
+ var scaleLevelIndex = useViewerState(select("scaleLevelIndex"));
53
58
  var changeViewerSetting = useViewerState(select("changeViewerSetting"));
54
59
 
55
60
  // Scroll buttons are only visible when toolbar can be scrolled in that direction.
@@ -244,7 +249,39 @@ var Toolbar = function Toolbar(props) {
244
249
  onClick: toggleBoundingBox
245
250
  }, /*#__PURE__*/React.createElement(ViewerIcon, {
246
251
  type: "boundingBox"
247
- }))))), /*#__PURE__*/React.createElement("div", {
252
+ })))), props.multiscaleDims !== undefined && props.multiscaleDims.length > 1 && /*#__PURE__*/React.createElement("div", {
253
+ className: "viewer-toolbar-group"
254
+ }, /*#__PURE__*/React.createElement("span", null, "Resolution"), /*#__PURE__*/React.createElement(Radio.Group, {
255
+ value: useExactScaleLevel,
256
+ onChange: function onChange(e) {
257
+ return changeViewerSetting("useExactScaleLevel", e.target.value);
258
+ }
259
+ }, /*#__PURE__*/React.createElement(Radio.Button, {
260
+ value: false
261
+ }, "Auto"), /*#__PURE__*/React.createElement(Radio.Button, {
262
+ value: true
263
+ }, "Manual")), /*#__PURE__*/React.createElement(Select, {
264
+ className: "select-render-setting",
265
+ style: {
266
+ minWidth: 150
267
+ },
268
+ value: useExactScaleLevel ? scaleLevelIndex : (_props$multiscaleInde = props.multiscaleIndex) !== null && _props$multiscaleInde !== void 0 ? _props$multiscaleInde : scaleLevelIndex,
269
+ onChange: function onChange(value) {
270
+ return changeViewerSetting("scaleLevelIndex", value);
271
+ },
272
+ disabled: !useExactScaleLevel
273
+ }, props.multiscaleDims.map(function (dims, idx) {
274
+ var _dims$shape = _slicedToArray(dims.shape, 5),
275
+ _t = _dims$shape[0],
276
+ _c = _dims$shape[1],
277
+ z = _dims$shape[2],
278
+ y = _dims$shape[3],
279
+ x = _dims$shape[4];
280
+ return /*#__PURE__*/React.createElement(Select.Option, {
281
+ key: idx,
282
+ value: idx
283
+ }, x, " x ", y, " x ", z);
284
+ })))), /*#__PURE__*/React.createElement("div", {
248
285
  className: "viewer-toolbar-right viewer-toolbar-group",
249
286
  ref: rightRef
250
287
  }, /*#__PURE__*/React.createElement(Tooltip, {
@@ -169,6 +169,27 @@
169
169
  transition:all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
170
170
  }
171
171
 
172
+ .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-item,
173
+ .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-search{
174
+ padding-inline-end:0;
175
+ }
176
+
177
+ .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-item{
178
+ color:var(--color-button-icon-disabled-text);
179
+ }
180
+
181
+ .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selection-item:hover{
182
+ color:var(--color-button-icon-disabled-text);
183
+ }
184
+
185
+ .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-selector{
186
+ border-color:var(--color-button-icon-disabled-text);
187
+ }
188
+
189
+ .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting.ant-select-disabled .ant-select-arrow{
190
+ display:none;
191
+ }
192
+
172
193
  .viewer-toolbar.viewer-toolbar .ant-select.select-render-setting .ant-select-selector{
173
194
  border-color:#aeacae;
174
195
  box-shadow:none;
@@ -8,13 +8,13 @@ function _regeneratorAsyncIterator(t, e) { function n(r, o, i, f) { try { var c
8
8
  function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
9
9
  function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
10
10
  function _OverloadYield(e, d) { this.v = e, this.k = d; }
11
- function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
12
- function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
13
11
  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; }
14
12
  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; }
15
13
  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; }
16
14
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
17
15
  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); }
16
+ function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }
17
+ function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
18
18
  function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
19
19
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
20
20
  function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
@@ -24,13 +24,12 @@ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
24
24
  import { LoadSpec, VolumeLoaderContext } from "@aics/vole-core";
25
25
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
26
26
  import { Box3, Vector3 } from "three";
27
- import { AXIS_TO_LOADER_PRIORITY, CACHE_MAX_SIZE, getDefaultChannelColor, getDefaultViewerChannelSettings, QUEUE_MAX_LOW_PRIORITY_SIZE, QUEUE_MAX_SIZE } from "../shared/constants";
27
+ import { AXIS_TO_LOADER_PRIORITY, CACHE_MAX_SIZE, getDefaultViewerChannelSettings, QUEUE_MAX_LOW_PRIORITY_SIZE, QUEUE_MAX_SIZE } from "../shared/constants";
28
28
  import { ViewMode } from "../shared/enums";
29
29
  import { useConstructor, useRefWithSetter } from "../shared/utils/hooks";
30
30
  import PlayControls from "../shared/utils/playControls";
31
31
  import SceneStore from "../shared/utils/sceneStore";
32
32
  import { makeChannelIndexGrouping } from "../shared/utils/viewerChannelSettings";
33
- import { initializeOneChannelSetting } from "../shared/utils/viewerState";
34
33
  import { select, useViewerState } from "../state/store";
35
34
  export var ImageLoadStatus = /*#__PURE__*/function (ImageLoadStatus) {
36
35
  ImageLoadStatus[ImageLoadStatus["REQUESTED"] = 0] = "REQUESTED";
@@ -57,10 +56,14 @@ var CHANNEL_RELOAD = 0;
57
56
  */
58
57
  var useEffectEventRef = function useEffectEventRef(callback) {
59
58
  var callbackRef = useRef(callback);
60
- useEffect(function () {
61
- callbackRef.current = callback;
62
- }, [callback]);
63
- return callbackRef;
59
+ callbackRef.current = callback;
60
+ return useCallback(function () {
61
+ var _callbackRef$current;
62
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
63
+ args[_key] = arguments[_key];
64
+ }
65
+ return (_callbackRef$current = callbackRef.current) === null || _callbackRef$current === void 0 ? void 0 : _callbackRef$current.call.apply(_callbackRef$current, [callbackRef].concat(args));
66
+ }, [callbackRef]);
64
67
  };
65
68
 
66
69
  /**
@@ -76,7 +79,7 @@ var useEffectEventRef = function useEffectEventRef(callback) {
76
79
  var useVolume = function useVolume(scenePaths, options) {
77
80
  var channelSettings = useViewerState(select("channelSettings"));
78
81
  var changeViewerSetting = useViewerState(select("changeViewerSetting"));
79
- var replaceAllChannelSettings = useViewerState(select("replaceAllChannelSettings"));
82
+ var initChannelSettings = useViewerState(select("initChannelSettings"));
80
83
  var onErrorRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onError);
81
84
  var onChannelLoadedRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onChannelLoaded);
82
85
  var onChangeSceneRef = useEffectEventRef(options === null || options === void 0 ? void 0 : options.onChangeScene);
@@ -173,9 +176,8 @@ var useVolume = function useVolume(scenePaths, options) {
173
176
  }));
174
177
  }, [channelVersionsRef, setChannelVersions]);
175
178
  var onError = useCallback(function (e) {
176
- var _onErrorRef$current;
177
179
  setLoadThrewError(true);
178
- (_onErrorRef$current = onErrorRef.current) === null || _onErrorRef$current === void 0 || _onErrorRef$current.call(onErrorRef, e);
180
+ onErrorRef(e);
179
181
  throw e;
180
182
  }, [onErrorRef]);
181
183
 
@@ -185,10 +187,9 @@ var useVolume = function useVolume(scenePaths, options) {
185
187
  channelGroupedByType = _useState10[0],
186
188
  setChannelGroupedByType = _useState10[1];
187
189
  var onChannelDataLoaded = useCallback(function (aimg, channelIndex) {
188
- var _onChannelLoadedRef$c;
189
190
  // let the hook caller know that this channel has loaded
190
191
  var isInitialLoad = channelVersionsRef.current[channelIndex] === CHANNEL_INITIAL_LOAD;
191
- (_onChannelLoadedRef$c = onChannelLoadedRef.current) === null || _onChannelLoadedRef$c === void 0 || _onChannelLoadedRef$c.call(onChannelLoadedRef, aimg, channelIndex, isInitialLoad);
192
+ onChannelLoadedRef(aimg, channelIndex, isInitialLoad);
192
193
 
193
194
  // set this channel as loaded
194
195
  var newVersions = channelVersionsRef.current.slice();
@@ -200,34 +201,23 @@ var useVolume = function useVolume(scenePaths, options) {
200
201
  playControls.onImageLoaded();
201
202
  }
202
203
  }, [channelVersionsRef, onChannelLoadedRef, playControls, setChannelVersions]);
204
+ var setChannelStateForNewImage = useCallback(function (channelNames) {
205
+ var _useViewerState$getSt = useViewerState.getState(),
206
+ useDefaultViewerChannelSettings = _useViewerState$getSt.useDefaultViewerChannelSettings;
207
+ var viewerChannelSettings = useDefaultViewerChannelSettings ? getDefaultViewerChannelSettings() : options === null || options === void 0 ? void 0 : options.viewerChannelSettings;
208
+ var grouping = makeChannelIndexGrouping(channelNames, viewerChannelSettings);
209
+ setChannelGroupedByType(grouping);
210
+ return initChannelSettings(channelNames, viewerChannelSettings);
211
+ }, [initChannelSettings, options === null || options === void 0 ? void 0 : options.viewerChannelSettings]);
203
212
 
204
213
  // effect to start the initial load of the image
205
214
  useEffect(function () {
206
- var channelSettings = useViewerState.getState().channelSettings;
207
215
  setChannelVersions(new Array(channelVersionsRef.current.length).fill(CHANNEL_INITIAL_LOAD));
208
216
  setLoadThrewError(false);
209
217
  inInitialLoadRef.current = true;
210
- var setChannelStateForNewImage = function setChannelStateForNewImage(channelNames) {
211
- var _useViewerState$getSt = useViewerState.getState(),
212
- useDefaultViewerChannelSettings = _useViewerState$getSt.useDefaultViewerChannelSettings;
213
- var viewerChannelSettings = useDefaultViewerChannelSettings ? getDefaultViewerChannelSettings() : options === null || options === void 0 ? void 0 : options.viewerChannelSettings;
214
- var grouping = makeChannelIndexGrouping(channelNames, viewerChannelSettings);
215
- setChannelGroupedByType(grouping);
216
- var newChannelSettings = channelNames.map(function (name, index) {
217
- var _channelSettings$inde;
218
- var color = getDefaultChannelColor(index);
219
- var channelSetting = (_channelSettings$inde = channelSettings[index]) !== null && _channelSettings$inde !== void 0 ? _channelSettings$inde : initializeOneChannelSetting(name, index, color, viewerChannelSettings);
220
- return _objectSpread(_objectSpread({}, channelSetting), {}, {
221
- name: name
222
- });
223
- });
224
- replaceAllChannelSettings(newChannelSettings);
225
- return newChannelSettings;
226
- };
227
218
  var openImage = /*#__PURE__*/function () {
228
219
  var _ref3 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
229
- var _onCreateImageRef$cur;
230
- var scene, time, loadSpec, aimg, channelNames, newChannelSettings, requiredLoadSpec, requiredChannelsToLoad, _useViewerState$getSt2, useDefaultViewerChannelSettings, viewerChannelSettings, maskChannelName, maskChannelIndex, viewMode, slice, onCreateScene;
220
+ var scene, time, loadSpec, aimg, channelNames, newChannelSettings, requiredLoadSpec, requiredChannelsToLoad, _useViewerState$getSt2, useDefaultViewerChannelSettings, viewerChannelSettings, maskChannelName, maskChannelIndex, viewMode, slice;
231
221
  return _regeneratorRuntime().wrap(function _callee$(_context) {
232
222
  while (1) switch (_context.prev = _context.next) {
233
223
  case 0:
@@ -243,7 +233,7 @@ var useVolume = function useVolume(scenePaths, options) {
243
233
  newChannelSettings = setChannelStateForNewImage(channelNames);
244
234
  setChannelVersions(new Array(channelNames.length).fill(CHANNEL_INITIAL_LOAD));
245
235
  setImage(aimg);
246
- (_onCreateImageRef$cur = onCreateImageRef.current) === null || _onCreateImageRef$cur === void 0 || _onCreateImageRef$cur.call(onCreateImageRef, aimg);
236
+ onCreateImageRef(aimg);
247
237
  playControls.stepAxis = function (axis) {
248
238
  var time = useViewerState.getState().time;
249
239
  var slice = useViewerState.getState().slice;
@@ -289,11 +279,10 @@ var useVolume = function useVolume(scenePaths, options) {
289
279
 
290
280
  // initiate loading only after setting up new channel settings,
291
281
  // in case the loader callback fires before the state is set
292
- onCreateScene = onChangeSceneRef.current;
293
282
  sceneLoader.loadScene(scene, aimg, requiredLoadSpec, {
294
- onCreateScene: onCreateScene
283
+ onCreateScene: onChangeSceneRef
295
284
  })["catch"](onError);
296
- case 27:
285
+ case 26:
297
286
  case "end":
298
287
  return _context.stop();
299
288
  }
@@ -304,7 +293,7 @@ var useVolume = function useVolume(scenePaths, options) {
304
293
  };
305
294
  }();
306
295
  openImage();
307
- }, [sceneLoader, onError, onCreateImageRef, onChangeSceneRef, onChannelLoadedRef, channelVersionsRef, setChannelVersions, playControls, setIsLoading, onChannelDataLoaded, changeViewerSetting, replaceAllChannelSettings, options === null || options === void 0 ? void 0 : options.viewerChannelSettings]);
296
+ }, [sceneLoader, onError, onCreateImageRef, onChangeSceneRef, onChannelLoadedRef, channelVersionsRef, setChannelVersions, playControls, setIsLoading, onChannelDataLoaded, changeViewerSetting, initChannelSettings, setChannelStateForNewImage, options === null || options === void 0 ? void 0 : options.viewerChannelSettings]);
308
297
  // of the above dependencies, we expect only `sceneLoader` to change.
309
298
 
310
299
  var setTime = useCallback(function (view3d, time) {
@@ -315,13 +304,26 @@ var useVolume = function useVolume(scenePaths, options) {
315
304
  }, [image, onError, setIsLoading, inInitialLoadRef]);
316
305
  var setScene = useCallback(function (scene) {
317
306
  if (image && !inInitialLoadRef.current) {
318
- var onCreateScene = onChangeSceneRef.current;
307
+ var onCreateScene = function onCreateScene(volume, sceneIndex, loadSpec) {
308
+ setChannelStateForNewImage(volume.imageInfo.channelNames);
309
+ volume.updateChannelCount();
310
+ var prevChannelVersions = channelVersionsRef.current;
311
+ var newChannelVersions;
312
+ var addedChannelCount = volume.imageInfo.numChannels - prevChannelVersions.length;
313
+ if (addedChannelCount > 0) {
314
+ newChannelVersions = prevChannelVersions.concat(Array(addedChannelCount).fill(CHANNEL_INITIAL_LOAD));
315
+ } else {
316
+ newChannelVersions = prevChannelVersions.slice(0, volume.imageInfo.numChannels);
317
+ }
318
+ setChannelVersions(newChannelVersions);
319
+ onChangeSceneRef(volume, sceneIndex, loadSpec);
320
+ };
319
321
  sceneLoader.loadScene(scene, image, undefined, {
320
322
  onCreateScene: onCreateScene
321
323
  })["catch"](onError);
322
324
  setIsLoading(LoadType.SCENE);
323
325
  }
324
- }, [image, onError, sceneLoader, setIsLoading, inInitialLoadRef, onChangeSceneRef]);
326
+ }, [image, sceneLoader, onError, setIsLoading, setChannelStateForNewImage, channelVersionsRef, setChannelVersions, onChangeSceneRef]);
325
327
  return useMemo(function () {
326
328
  return {
327
329
  image: image,
@@ -166,6 +166,8 @@ export var getDefaultViewerState = function getDefaultViewerState() {
166
166
  scene: 0,
167
167
  singleChannelMode: false,
168
168
  singleChannelIndex: 0,
169
+ useExactScaleLevel: false,
170
+ scaleLevelIndex: 0,
169
171
  // Do not override camera position, target, etc. by default;
170
172
  // instead, let the viewer apply default camera settings based on the view mode.
171
173
  // This prevents a bug where the camera's position and view mode are set to
@@ -1,3 +1,5 @@
1
+ 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; }
2
+ 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; }
1
3
  function _regeneratorRuntime() { "use strict"; var r = _regenerator(), e = r.m(_regeneratorRuntime), t = (Object.getPrototypeOf ? Object.getPrototypeOf(e) : e.__proto__).constructor; function n(r) { var e = "function" == typeof r && r.constructor; return !!e && (e === t || "GeneratorFunction" === (e.displayName || e.name)); } var o = { "throw": 1, "return": 2, "break": 3, "continue": 3 }; function a(r) { var e, t; return function (n) { e || (e = { stop: function stop() { return t(n.a, 2); }, "catch": function _catch() { return n.v; }, abrupt: function abrupt(r, e) { return t(n.a, o[r], e); }, delegateYield: function delegateYield(r, o, a) { return e.resultName = o, t(n.d, _regeneratorValues(r), a); }, finish: function finish(r) { return t(n.f, r); } }, t = function t(r, _t, o) { n.p = e.prev, n.n = e.next; try { return r(_t, o); } finally { e.next = n.n; } }), e.resultName && (e[e.resultName] = n.v, e.resultName = void 0), e.sent = n.v, e.next = n.n; try { return r.call(this, e); } finally { n.p = e.prev, n.n = e.next; } }; } return (_regeneratorRuntime = function _regeneratorRuntime() { return { wrap: function wrap(e, t, n, o) { return r.w(a(e), t, n, o && o.reverse()); }, isGeneratorFunction: n, mark: r.m, awrap: function awrap(r, e) { return new _OverloadYield(r, e); }, AsyncIterator: _regeneratorAsyncIterator, async: function async(r, e, t, o, u) { return (n(e) ? _regeneratorAsyncGen : _regeneratorAsync)(a(r), e, t, o, u); }, keys: _regeneratorKeys, values: _regeneratorValues }; })(); }
2
4
  function _regeneratorValues(e) { if (null != e) { var t = e["function" == typeof Symbol && Symbol.iterator || "@@iterator"], r = 0; if (t) return t.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) return { next: function next() { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e }; } }; } throw new TypeError(_typeof(e) + " is not iterable"); }
3
5
  function _regeneratorKeys(e) { var n = Object(e), r = []; for (var t in n) r.unshift(t); return function e() { for (; r.length;) if ((t = r.pop()) in n) return e.value = t, e.done = !1, e; return e.done = !0, e; }; }
@@ -16,7 +18,7 @@ function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r),
16
18
  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; }
17
19
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
18
20
  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); }
19
- import { VolumeFileFormat } from "@aics/vole-core";
21
+ import { createDefaultMetadata, VolumeFileFormat } from "@aics/vole-core";
20
22
  var SceneStore = /*#__PURE__*/function () {
21
23
  function SceneStore(context, paths) {
22
24
  _classCallCheck(this, SceneStore);
@@ -77,8 +79,8 @@ var SceneStore = /*#__PURE__*/function () {
77
79
  key: "loadScene",
78
80
  value: function () {
79
81
  var _loadScene = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(scene, image, loadSpec, options) {
80
- var _options$onCreateScen;
81
- var loader, spec;
82
+ var _spec$channels, _options$onCreateScen;
83
+ var loader, spec, imageInfo, maxTime, adjustedSpec;
82
84
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
83
85
  while (1) switch (_context2.prev = _context2.next) {
84
86
  case 0:
@@ -91,10 +93,19 @@ var SceneStore = /*#__PURE__*/function () {
91
93
  _context2.next = 7;
92
94
  return loader.createImageInfo(spec);
93
95
  case 7:
94
- image.imageInfo.imageInfo = _context2.sent.imageInfo;
95
- options === null || options === void 0 || (_options$onCreateScen = options.onCreateScene) === null || _options$onCreateScen === void 0 || _options$onCreateScen.call(options, image, scene);
96
- loader.loadVolumeData(image, spec, options === null || options === void 0 ? void 0 : options.onChannelLoaded);
97
- case 10:
96
+ imageInfo = _context2.sent.imageInfo;
97
+ image.imageInfo.imageInfo = imageInfo;
98
+ image.imageMetadata = createDefaultMetadata(imageInfo);
99
+ maxTime = imageInfo.multiscaleLevelDims[imageInfo.multiscaleLevel].shape[0] - 1;
100
+ adjustedSpec = _objectSpread(_objectSpread({}, spec), {}, {
101
+ channels: (_spec$channels = spec.channels) === null || _spec$channels === void 0 ? void 0 : _spec$channels.filter(function (channelIndex) {
102
+ return channelIndex < imageInfo.channelNames.length;
103
+ }),
104
+ time: Math.min(spec.time, maxTime)
105
+ });
106
+ options === null || options === void 0 || (_options$onCreateScen = options.onCreateScene) === null || _options$onCreateScen === void 0 || _options$onCreateScen.call(options, image, scene, adjustedSpec);
107
+ loader.loadVolumeData(image, adjustedSpec, options === null || options === void 0 ? void 0 : options.onChannelLoaded);
108
+ case 14:
98
109
  case "end":
99
110
  return _context2.stop();
100
111
  }
@@ -438,7 +438,9 @@ describe("Viewer state", function () {
438
438
  scene: 0,
439
439
  cameraState: undefined,
440
440
  singleChannelMode: false,
441
- singleChannelIndex: 0
441
+ singleChannelIndex: 0,
442
+ useExactScaleLevel: false,
443
+ scaleLevelIndex: 0
442
444
  };
443
445
  var SERIALIZED_DEFAULT_VIEWER_STATE = {
444
446
  mode: "volumetric",
@@ -459,7 +461,9 @@ describe("Viewer state", function () {
459
461
  t: "0",
460
462
  scene: "0",
461
463
  scm: "0",
462
- sci: "0"
464
+ sci: "0",
465
+ esl: "0",
466
+ scl: "0"
463
467
  };
464
468
  var CUSTOM_VIEWER_STATE = {
465
469
  renderMode: RenderMode.pathTrace,
@@ -489,6 +493,8 @@ describe("Viewer state", function () {
489
493
  scene: 3,
490
494
  singleChannelMode: true,
491
495
  singleChannelIndex: 3,
496
+ useExactScaleLevel: true,
497
+ scaleLevelIndex: 3,
492
498
  cameraState: {
493
499
  position: [-1.05, -4, 45],
494
500
  target: [0, 0, 0],
@@ -517,6 +523,8 @@ describe("Viewer state", function () {
517
523
  scene: "3",
518
524
  scm: "1",
519
525
  sci: "3",
526
+ esl: "1",
527
+ scl: "3",
520
528
  cam: "pos:-1.05:-4:45,tar:0:0:0,up:0:1:0,ort:3.534,fov:43.5"
521
529
  };
522
530
  describe("serializeViewerState", function () {
@@ -1,5 +1,5 @@
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
- var _ViewerChannelSetting, _ViewerChannelSetting2, _ViewerChannelSetting3, _ViewerChannelSetting4, _ViewerChannelSetting5, _ViewerChannelSetting6, _ViewerChannelSetting7, _ViewerChannelSetting8, _ViewerChannelSetting9, _ViewerChannelSetting10, _ViewerChannelSetting11, _ViewerChannelSetting12, _ViewerChannelSetting13, _ViewerChannelSetting14, _ViewerStateKeys$View, _ViewerStateKeys$Mode, _ViewerStateKeys$Mask, _ViewerStateKeys$Imag, _ViewerStateKeys$Axes, _ViewerStateKeys$Boun, _ViewerStateKeys$Sing, _ViewerStateKeys$Sing2, _ViewerStateKeys$Boun2, _ViewerStateKeys$Back, _ViewerStateKeys$Auto, _ViewerStateKeys$Brig, _ViewerStateKeys$Dens, _ViewerStateKeys$Leve, _ViewerStateKeys$Inte, _ViewerStateKeys$Regi, _ViewerStateKeys$Slic, _ViewerStateKeys$Time, _ViewerStateKeys$Scen, _ViewerStateKeys$Came;
2
+ var _ViewerChannelSetting, _ViewerChannelSetting2, _ViewerChannelSetting3, _ViewerChannelSetting4, _ViewerChannelSetting5, _ViewerChannelSetting6, _ViewerChannelSetting7, _ViewerChannelSetting8, _ViewerChannelSetting9, _ViewerChannelSetting10, _ViewerChannelSetting11, _ViewerChannelSetting12, _ViewerChannelSetting13, _ViewerChannelSetting14, _ViewerStateKeys$View, _ViewerStateKeys$Mode, _ViewerStateKeys$Mask, _ViewerStateKeys$Imag, _ViewerStateKeys$Axes, _ViewerStateKeys$Boun, _ViewerStateKeys$Sing, _ViewerStateKeys$Sing2, _ViewerStateKeys$Boun2, _ViewerStateKeys$Back, _ViewerStateKeys$Auto, _ViewerStateKeys$Brig, _ViewerStateKeys$Dens, _ViewerStateKeys$Leve, _ViewerStateKeys$Inte, _ViewerStateKeys$Regi, _ViewerStateKeys$Slic, _ViewerStateKeys$Time, _ViewerStateKeys$Scen, _ViewerStateKeys$UseE, _ViewerStateKeys$Scal, _ViewerStateKeys$Came;
3
3
  function _regeneratorRuntime() { "use strict"; var r = _regenerator(), e = r.m(_regeneratorRuntime), t = (Object.getPrototypeOf ? Object.getPrototypeOf(e) : e.__proto__).constructor; function n(r) { var e = "function" == typeof r && r.constructor; return !!e && (e === t || "GeneratorFunction" === (e.displayName || e.name)); } var o = { "throw": 1, "return": 2, "break": 3, "continue": 3 }; function a(r) { var e, t; return function (n) { e || (e = { stop: function stop() { return t(n.a, 2); }, "catch": function _catch() { return n.v; }, abrupt: function abrupt(r, e) { return t(n.a, o[r], e); }, delegateYield: function delegateYield(r, o, a) { return e.resultName = o, t(n.d, _regeneratorValues(r), a); }, finish: function finish(r) { return t(n.f, r); } }, t = function t(r, _t, o) { n.p = e.prev, n.n = e.next; try { return r(_t, o); } finally { e.next = n.n; } }), e.resultName && (e[e.resultName] = n.v, e.resultName = void 0), e.sent = n.v, e.next = n.n; try { return r.call(this, e); } finally { n.p = e.prev, n.n = e.next; } }; } return (_regeneratorRuntime = function _regeneratorRuntime() { return { wrap: function wrap(e, t, n, o) { return r.w(a(e), t, n, o && o.reverse()); }, isGeneratorFunction: n, mark: r.m, awrap: function awrap(r, e) { return new _OverloadYield(r, e); }, AsyncIterator: _regeneratorAsyncIterator, async: function async(r, e, t, o, u) { return (n(e) ? _regeneratorAsyncGen : _regeneratorAsync)(a(r), e, t, o, u); }, keys: _regeneratorKeys, values: _regeneratorValues }; })(); }
4
4
  function _regeneratorValues(e) { if (null != e) { var t = e["function" == typeof Symbol && Symbol.iterator || "@@iterator"], r = 0; if (t) return t.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) return { next: function next() { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e }; } }; } throw new TypeError(_typeof(e) + " is not iterable"); }
5
5
  function _regeneratorKeys(e) { var n = Object(e), r = []; for (var t in n) r.unshift(t); return function e() { for (; r.length;) if ((t = r.pop()) in n) return e.value = t, e.done = !1, e; return e.done = !0, e; }; }
@@ -113,6 +113,8 @@ export var ViewerStateKeys = /*#__PURE__*/function (ViewerStateKeys) {
113
113
  ViewerStateKeys["CameraState"] = "cam";
114
114
  ViewerStateKeys["SingleChannelMode"] = "scm";
115
115
  ViewerStateKeys["SingleChannelIndex"] = "sci";
116
+ ViewerStateKeys["UseExactScaleLevel"] = "esl";
117
+ ViewerStateKeys["ScaleLevelIndex"] = "scl";
116
118
  return ViewerStateKeys;
117
119
  }({});
118
120
  export var CameraTransformKeys = /*#__PURE__*/function (CameraTransformKeys) {
@@ -272,6 +274,8 @@ _ViewerStateKeys$Regi = ViewerStateKeys.Region;
272
274
  _ViewerStateKeys$Slic = ViewerStateKeys.Slice;
273
275
  _ViewerStateKeys$Time = ViewerStateKeys.Time;
274
276
  _ViewerStateKeys$Scen = ViewerStateKeys.Scene;
277
+ _ViewerStateKeys$UseE = ViewerStateKeys.UseExactScaleLevel;
278
+ _ViewerStateKeys$Scal = ViewerStateKeys.ScaleLevelIndex;
275
279
  _ViewerStateKeys$Came = ViewerStateKeys.CameraState;
276
280
  /** Serialized version of `ViewerState`. */
277
281
  export var ViewerStateParams = /*#__PURE__*/_createClass(function ViewerStateParams() {
@@ -325,6 +329,10 @@ export var ViewerStateParams = /*#__PURE__*/_createClass(function ViewerStatePar
325
329
  _defineProperty(this, _ViewerStateKeys$Time, undefined);
326
330
  /** Scene number, for multiscene images. 0 by default. */
327
331
  _defineProperty(this, _ViewerStateKeys$Scen, undefined);
332
+ /** Whether to use an exact scale level index. 0 by default. */
333
+ _defineProperty(this, _ViewerStateKeys$UseE, undefined);
334
+ /** The exact scale level index to use, if `UseExactScaleLevel` is 1. 0 by default. */
335
+ _defineProperty(this, _ViewerStateKeys$Scal, undefined);
328
336
  /**
329
337
  * Camera transform settings, as a list of `key:value` pairs separated by commas.
330
338
  * Valid keys are defined in `CameraTransformKeys`:
@@ -952,6 +960,8 @@ export function deserializeViewerState(params) {
952
960
  renderMode: parseStringEnum(params[ViewerStateKeys.Mode], RenderMode),
953
961
  singleChannelMode: parseStringBoolean(params[ViewerStateKeys.SingleChannelMode]),
954
962
  singleChannelIndex: parseStringInt(params[ViewerStateKeys.SingleChannelIndex], 0, Number.POSITIVE_INFINITY),
963
+ useExactScaleLevel: parseStringBoolean(params[ViewerStateKeys.UseExactScaleLevel]),
964
+ scaleLevelIndex: parseStringInt(params[ViewerStateKeys.ScaleLevelIndex], 0, Number.MAX_SAFE_INTEGER),
955
965
  cameraState: parseCameraState(params[ViewerStateKeys.CameraState])
956
966
  };
957
967
 
@@ -983,11 +993,15 @@ export function deserializeViewerState(params) {
983
993
  * @returns A `ViewerStateParams` object with the serialized parameters. Undefined values are removed.
984
994
  */
985
995
  export function serializeViewerState(state, removeDefaults) {
986
- var _state$maskAlpha, _state$brightness, _state$density, _state$levels, _state$time, _state$scene, _state$singleChannelI, _result;
996
+ var _state$maskAlpha, _state$brightness, _state$density, _state$levels, _state$time, _state$scene, _state$singleChannelI, _state$scaleLevelInde, _result;
987
997
  if (removeDefaults) {
988
998
  state = removeMatchingProperties(state, getDefaultViewerState());
999
+ // special case: if there's an explicit scale level but it's not being used, no reason to include it
1000
+ if (state.scaleLevelIndex !== undefined && state.useExactScaleLevel === undefined) {
1001
+ delete state.scaleLevelIndex;
1002
+ }
989
1003
  }
990
- var result = (_result = {}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_result, ViewerStateKeys.Mode, state.renderMode), ViewerStateKeys.Mask, (_state$maskAlpha = state.maskAlpha) === null || _state$maskAlpha === void 0 ? void 0 : _state$maskAlpha.toString()), ViewerStateKeys.Image, state.imageType), ViewerStateKeys.Axes, serializeBoolean(state.showAxes)), ViewerStateKeys.BoundingBox, serializeBoolean(state.showBoundingBox)), ViewerStateKeys.BoundingBoxColor, state.boundingBoxColor && colorArrayToHex(state.boundingBoxColor)), ViewerStateKeys.BackgroundColor, state.backgroundColor && colorArrayToHex(state.backgroundColor)), ViewerStateKeys.Autorotate, serializeBoolean(state.autorotate)), ViewerStateKeys.Brightness, (_state$brightness = state.brightness) === null || _state$brightness === void 0 ? void 0 : _state$brightness.toString()), ViewerStateKeys.Density, (_state$density = state.density) === null || _state$density === void 0 ? void 0 : _state$density.toString()), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_result, ViewerStateKeys.Interpolation, serializeBoolean(state.interpolationEnabled)), ViewerStateKeys.Region, state.region && serializeRegion(state.region)), ViewerStateKeys.Slice, state.slice && serializeSlice(state.slice)), ViewerStateKeys.Levels, (_state$levels = state.levels) === null || _state$levels === void 0 ? void 0 : _state$levels.join(",")), ViewerStateKeys.Time, (_state$time = state.time) === null || _state$time === void 0 ? void 0 : _state$time.toString()), ViewerStateKeys.Scene, (_state$scene = state.scene) === null || _state$scene === void 0 ? void 0 : _state$scene.toString()), ViewerStateKeys.SingleChannelMode, serializeBoolean(state.singleChannelMode)), ViewerStateKeys.SingleChannelIndex, (_state$singleChannelI = state.singleChannelIndex) === null || _state$singleChannelI === void 0 ? void 0 : _state$singleChannelI.toString()), ViewerStateKeys.CameraState, state.cameraState && serializeCameraState(state.cameraState, removeDefaults, state.viewMode)));
1004
+ var result = (_result = {}, _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_result, ViewerStateKeys.Mode, state.renderMode), ViewerStateKeys.Mask, (_state$maskAlpha = state.maskAlpha) === null || _state$maskAlpha === void 0 ? void 0 : _state$maskAlpha.toString()), ViewerStateKeys.Image, state.imageType), ViewerStateKeys.Axes, serializeBoolean(state.showAxes)), ViewerStateKeys.BoundingBox, serializeBoolean(state.showBoundingBox)), ViewerStateKeys.BoundingBoxColor, state.boundingBoxColor && colorArrayToHex(state.boundingBoxColor)), ViewerStateKeys.BackgroundColor, state.backgroundColor && colorArrayToHex(state.backgroundColor)), ViewerStateKeys.Autorotate, serializeBoolean(state.autorotate)), ViewerStateKeys.Brightness, (_state$brightness = state.brightness) === null || _state$brightness === void 0 ? void 0 : _state$brightness.toString()), ViewerStateKeys.Density, (_state$density = state.density) === null || _state$density === void 0 ? void 0 : _state$density.toString()), _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_result, ViewerStateKeys.Interpolation, serializeBoolean(state.interpolationEnabled)), ViewerStateKeys.Region, state.region && serializeRegion(state.region)), ViewerStateKeys.Slice, state.slice && serializeSlice(state.slice)), ViewerStateKeys.Levels, (_state$levels = state.levels) === null || _state$levels === void 0 ? void 0 : _state$levels.join(",")), ViewerStateKeys.Time, (_state$time = state.time) === null || _state$time === void 0 ? void 0 : _state$time.toString()), ViewerStateKeys.Scene, (_state$scene = state.scene) === null || _state$scene === void 0 ? void 0 : _state$scene.toString()), ViewerStateKeys.SingleChannelMode, serializeBoolean(state.singleChannelMode)), ViewerStateKeys.SingleChannelIndex, (_state$singleChannelI = state.singleChannelIndex) === null || _state$singleChannelI === void 0 ? void 0 : _state$singleChannelI.toString()), ViewerStateKeys.UseExactScaleLevel, serializeBoolean(state.useExactScaleLevel)), ViewerStateKeys.ScaleLevelIndex, (_state$scaleLevelInde = state.scaleLevelIndex) === null || _state$scaleLevelInde === void 0 ? void 0 : _state$scaleLevelInde.toString()), _defineProperty(_result, ViewerStateKeys.CameraState, state.cameraState && serializeCameraState(state.cameraState, removeDefaults, state.viewMode)));
991
1005
  var viewModeToViewParam = _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, ViewMode.threeD, "3D"), ViewMode.xy, "Z"), ViewMode.xz, "Y"), ViewMode.yz, "X");
992
1006
  result[ViewerStateKeys.View] = state.viewMode && viewModeToViewParam[state.viewMode];
993
1007
  return removeUndefinedProperties(result);
@@ -6,14 +6,15 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
6
6
  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); }
7
7
  import { create } from "zustand";
8
8
  import { subscribeWithSelector } from "zustand/middleware";
9
- import { getDefaultViewerState } from "../shared/constants";
9
+ import { getDefaultChannelColor, getDefaultViewerState } from "../shared/constants";
10
+ import { initializeOneChannelSetting } from "../shared/utils/viewerState";
10
11
  import { createResetSlice } from "./reset";
11
12
  import { validateState, validateStateValue } from "./util";
12
- var createViewerStateStore = function createViewerStateStore(set) {
13
- for (var _len = arguments.length, etc = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
14
- etc[_key - 1] = arguments[_key];
13
+ var createViewerStateStore = function createViewerStateStore(set, get) {
14
+ for (var _len = arguments.length, etc = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
15
+ etc[_key - 2] = arguments[_key];
15
16
  }
16
- return _objectSpread(_objectSpread(_objectSpread({}, createResetSlice.apply(void 0, [set].concat(etc))), getDefaultViewerState()), {}, {
17
+ return _objectSpread(_objectSpread(_objectSpread({}, createResetSlice.apply(void 0, [set, get].concat(etc))), getDefaultViewerState()), {}, {
17
18
  channelSettings: [],
18
19
  changeViewerSetting: function changeViewerSetting(key, value) {
19
20
  return set(function (state) {
@@ -41,6 +42,21 @@ var createViewerStateStore = function createViewerStateStore(set) {
41
42
  channelSettings: channelSettings
42
43
  });
43
44
  },
45
+ initChannelSettings: function initChannelSettings(names, settings) {
46
+ var currentSettings = get().channelSettings;
47
+ var channelSettings = names.map(function (name, index) {
48
+ var _currentSettings$inde;
49
+ var color = getDefaultChannelColor(index);
50
+ var channelSetting = (_currentSettings$inde = currentSettings[index]) !== null && _currentSettings$inde !== void 0 ? _currentSettings$inde : initializeOneChannelSetting(name, index, color, settings);
51
+ return _objectSpread(_objectSpread({}, channelSetting), {}, {
52
+ name: name
53
+ });
54
+ });
55
+ set({
56
+ channelSettings: channelSettings
57
+ });
58
+ return channelSettings;
59
+ },
44
60
  applyColorPresets: function applyColorPresets(colors) {
45
61
  set(function (_ref2) {
46
62
  var channelSettings = _ref2.channelSettings;
@@ -77,7 +93,9 @@ export var selectViewerSettings = function selectViewerSettings(store) {
77
93
  scene: store.scene,
78
94
  cameraState: store.cameraState,
79
95
  singleChannelMode: store.singleChannelMode,
80
- singleChannelIndex: store.singleChannelIndex
96
+ singleChannelIndex: store.singleChannelIndex,
97
+ useExactScaleLevel: store.useExactScaleLevel,
98
+ scaleLevelIndex: store.scaleLevelIndex
81
99
  };
82
100
  };
83
101
 
@@ -139,7 +139,28 @@ export var subscribeImageToState = function subscribeImageToState(store, view3d,
139
139
  // interpolation
140
140
  store.subscribe(select("interpolationEnabled"), function (enabled) {
141
141
  return view3d.setInterpolationEnabled(image, enabled);
142
- }, REF_EQ),
142
+ }, REF_EQ), store.subscribe(function (_ref3) {
143
+ var useExactScaleLevel = _ref3.useExactScaleLevel,
144
+ scaleLevelIndex = _ref3.scaleLevelIndex;
145
+ return {
146
+ useExactScaleLevel: useExactScaleLevel,
147
+ scaleLevelIndex: scaleLevelIndex
148
+ };
149
+ }, function (_ref4) {
150
+ var useExactScaleLevel = _ref4.useExactScaleLevel,
151
+ scaleLevelIndex = _ref4.scaleLevelIndex;
152
+ if (useExactScaleLevel) {
153
+ image.updateRequiredData({
154
+ useExplicitLevel: true,
155
+ multiscaleLevel: scaleLevelIndex
156
+ });
157
+ } else {
158
+ image.updateRequiredData({
159
+ useExplicitLevel: false,
160
+ multiscaleLevel: undefined
161
+ });
162
+ }
163
+ }, DEEP_EQ),
143
164
  // clipping
144
165
  store.subscribe(selectAxisClipUpdateInfo("x"), axisClipUpdater("x"), DEEP_EQ), store.subscribe(selectAxisClipUpdateInfo("y"), axisClipUpdater("y"), DEEP_EQ), store.subscribe(selectAxisClipUpdateInfo("z"), axisClipUpdater("z"), DEEP_EQ)
145
166
 
@@ -40,7 +40,9 @@ var arbitraryViewerState = function arbitraryViewerState() {
40
40
  scene: 3,
41
41
  singleChannelMode: true,
42
42
  singleChannelIndex: 3,
43
- cameraState: undefined
43
+ cameraState: undefined,
44
+ useExactScaleLevel: true,
45
+ scaleLevelIndex: 3
44
46
  };
45
47
  };
46
48
  var arbitraryChannelState = function arbitraryChannelState() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aics/vole-app",
3
- "version": "3.2.3",
3
+ "version": "3.3.0",
4
4
  "description": "web view of cell volume images",
5
5
  "main": "es/index.js",
6
6
  "module": "es/index.js",
@@ -45,7 +45,7 @@
45
45
  "author": "Megan Riel-Mehan",
46
46
  "license": "ISC",
47
47
  "dependencies": {
48
- "@aics/vole-core": "^4.6.4",
48
+ "@aics/vole-core": "^4.8.2",
49
49
  "@ant-design/icons": "^5.2.5",
50
50
  "@fortawesome/fontawesome-svg-core": "^6.5.2",
51
51
  "@fortawesome/free-solid-svg-icons": "^6.5.2",
@@ -1,3 +1,4 @@
1
+ import type { VolumeDims } from "@aics/vole-core/es/types/VolumeDims";
1
2
  import React from "react";
2
3
  import "./styles.css";
3
4
  type ToolbarProps = {
@@ -6,6 +7,8 @@ type ToolbarProps = {
6
7
  hasCellId: boolean;
7
8
  hasParentImage: boolean;
8
9
  canPathTrace: boolean;
10
+ multiscaleDims?: VolumeDims[];
11
+ multiscaleIndex?: number;
9
12
  resetCamera: () => void;
10
13
  downloadScreenshot: () => void;
11
14
  resetToSavedViewerState: () => void;
@@ -1,4 +1,4 @@
1
- import { type RawArrayLoaderOptions, type View3d, type Volume } from "@aics/vole-core";
1
+ import { LoadSpec, type RawArrayLoaderOptions, type View3d, type Volume } from "@aics/vole-core";
2
2
  import type { AxisName } from "../shared/types";
3
3
  import PlayControls from "../shared/utils/playControls";
4
4
  import type { ChannelGrouping, ViewerChannelSettings } from "../shared/utils/viewerChannelSettings";
@@ -7,7 +7,7 @@ export type UseVolumeOptions = {
7
7
  /** Callback called just once when the volume is created. */
8
8
  onCreateImage?: (image: Volume) => void;
9
9
  /** Callback called on any scene change, including the initial image load. */
10
- onChangeScene?: (image: Volume, sceneIndex: number) => void;
10
+ onChangeScene?: (image: Volume, sceneIndex: number, loadSpec: LoadSpec) => void;
11
11
  /** Callback for when a single channel of the volume has loaded. */
12
12
  onChannelLoaded?: (image: Volume, channelIndex: number, isInitialLoad: boolean) => void;
13
13
  /** Callback for when image loading encounters an error. */
@@ -1,7 +1,7 @@
1
1
  import type { LoadSpec, PrefetchDirection, RawArrayLoaderOptions, Volume, VolumeLoaderContext } from "@aics/vole-core";
2
2
  import type { ThreadableVolumeLoader } from "@aics/vole-core/es/types/loaders/IVolumeLoader";
3
3
  export type LoadSceneOptions = {
4
- onCreateScene?: (volume: Volume, sceneIndex: number) => void;
4
+ onCreateScene?: (volume: Volume, sceneIndex: number, loadSpec: LoadSpec) => void;
5
5
  onChannelLoaded?: (volume: Volume, channelIndex: number) => void;
6
6
  };
7
7
  export default class SceneStore {
@@ -50,7 +50,9 @@ export declare enum ViewerStateKeys {
50
50
  Scene = "scene",
51
51
  CameraState = "cam",
52
52
  SingleChannelMode = "scm",
53
- SingleChannelIndex = "sci"
53
+ SingleChannelIndex = "sci",
54
+ UseExactScaleLevel = "esl",
55
+ ScaleLevelIndex = "scl"
54
56
  }
55
57
  export declare enum CameraTransformKeys {
56
58
  /** Camera position in 3D coordinates. */
@@ -230,6 +232,10 @@ export declare class ViewerStateParams {
230
232
  [ViewerStateKeys.Time]?: string;
231
233
  /** Scene number, for multiscene images. 0 by default. */
232
234
  [ViewerStateKeys.Scene]?: string;
235
+ /** Whether to use an exact scale level index. 0 by default. */
236
+ [ViewerStateKeys.UseExactScaleLevel]?: string;
237
+ /** The exact scale level index to use, if `UseExactScaleLevel` is 1. 0 by default. */
238
+ [ViewerStateKeys.ScaleLevelIndex]?: string;
233
239
  /**
234
240
  * Camera transform settings, as a list of `key:value` pairs separated by commas.
235
241
  * Valid keys are defined in `CameraTransformKeys`:
@@ -1,4 +1,5 @@
1
1
  import type { ColorArray } from "../shared/utils/colorRepresentations";
2
+ import type { ViewerChannelSettings } from "../shared/utils/viewerChannelSettings";
2
3
  import { type ResetStateSlice } from "./reset";
3
4
  import type { ChannelState, ViewerState } from "./types";
4
5
  export type ViewerStateActions = {
@@ -8,6 +9,7 @@ export type ViewerStateActions = {
8
9
  }>) => void;
9
10
  changeChannelSetting: <K extends keyof ChannelState>(index: number | number[], value: Partial<Record<K, ChannelState[K]>>) => void;
10
11
  replaceAllChannelSettings: (channelSettings: ChannelState[]) => void;
12
+ initChannelSettings: (names: string[], settings?: ViewerChannelSettings) => ChannelState[];
11
13
  applyColorPresets: (colors: ColorArray[]) => void;
12
14
  };
13
15
  export type ViewerStore = ViewerState & ViewerStateActions & ResetStateSlice & {
@@ -24,6 +24,8 @@ export type ViewerState = {
24
24
  cameraState: Partial<CameraState> | undefined;
25
25
  singleChannelMode: boolean;
26
26
  singleChannelIndex: number;
27
+ useExactScaleLevel: boolean;
28
+ scaleLevelIndex: number;
27
29
  };
28
30
  export type ChannelState = {
29
31
  name: string;