@aics/vole-app 2.14.0 → 2.15.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.
File without changes
@@ -26,6 +26,7 @@ import { colorArrayToFloats } from "../../shared/utils/colorRepresentations";
26
26
  import { controlPointsToRamp, initializeLut, rampToControlPoints, remapControlPointsForChannel } from "../../shared/utils/controlPointsToLut";
27
27
  import { useConstructor } from "../../shared/utils/hooks";
28
28
  import { alphaSliderToImageValue, brightnessSliderToImageValue, densitySliderToImageValue, gammaSliderToImageValues } from "../../shared/utils/sliderValuesToImageValues";
29
+ import { findFirstChannelMatch } from "../../shared/utils/viewerChannelSettings";
29
30
  import useVolume, { ImageLoadStatus } from "../useVolume";
30
31
  import CellViewerCanvasWrapper from "../CellViewerCanvasWrapper";
31
32
  import ControlPanel from "../ControlPanel";
@@ -185,14 +186,41 @@ var App = function App(props) {
185
186
  // we need to keep track of channel ranges for remapping control points
186
187
  var channelRangesRef = useRef([]);
187
188
  var onCreateImage = useCallback(function (newImage) {
189
+ var _newImage$imageInfo$c;
188
190
  if (newImage === null) {
189
191
  return;
190
192
  }
191
- channelRangesRef.current = new Array(newImage.channelNames.length).fill(undefined);
193
+ var channelNames = newImage.channelNames;
194
+ channelRangesRef.current = new Array(channelNames.length).fill(undefined);
192
195
  var channelSettings = viewerState.current.channelSettings;
196
+
197
+ // If the image has channel color metadata, apply those colors now
198
+ var viewerChannelSettings = getCurrentViewerChannelSettings();
199
+ var channelColorMeta = (_newImage$imageInfo$c = newImage.imageInfo.channelColors) === null || _newImage$imageInfo$c === void 0 ? void 0 : _newImage$imageInfo$c.map(function (color, index) {
200
+ // Filter out channels that have colors in `viewerChannelSettings`
201
+ if (viewerChannelSettings === undefined) {
202
+ return color;
203
+ }
204
+ var settings = findFirstChannelMatch(channelNames[index], index, viewerChannelSettings);
205
+ if ((settings === null || settings === void 0 ? void 0 : settings.color) !== undefined) {
206
+ return undefined;
207
+ } else {
208
+ return color;
209
+ }
210
+ });
211
+ if (Array.isArray(channelColorMeta)) {
212
+ channelColorMeta.forEach(function (color, index) {
213
+ if (Array.isArray(color) && index < channelNames.length) {
214
+ changeChannelSetting(index, {
215
+ color: color
216
+ });
217
+ }
218
+ });
219
+ }
193
220
  view3d.addVolume(newImage, {
194
221
  // Immediately passing down channel parameters isn't strictly necessary, but keeps things looking consistent on load
195
- channels: newImage.channelNames.map(function (name) {
222
+ channels: newImage.channelNames.map(function (name, index) {
223
+ var _channelColorMeta$ind;
196
224
  // TODO do we really need to be searching by name here?
197
225
  var ch = channelSettings.find(function (channel) {
198
226
  return channel.name === name;
@@ -205,13 +233,13 @@ var App = function App(props) {
205
233
  isosurfaceEnabled: ch.isosurfaceEnabled,
206
234
  isovalue: ch.isovalue,
207
235
  isosurfaceOpacity: ch.opacity,
208
- color: ch.color
236
+ color: (_channelColorMeta$ind = channelColorMeta === null || channelColorMeta === void 0 ? void 0 : channelColorMeta[index]) !== null && _channelColorMeta$ind !== void 0 ? _channelColorMeta$ind : ch.color
209
237
  };
210
238
  })
211
239
  });
212
240
  onImageTitleChange === null || onImageTitleChange === void 0 || onImageTitleChange(newImage.imageInfo.imageInfo.name);
213
241
  view3d.updateActiveChannels(newImage);
214
- }, [view3d, viewerState, onImageTitleChange]);
242
+ }, [view3d, viewerState, onImageTitleChange, changeChannelSetting, getCurrentViewerChannelSettings]);
215
243
  var onChannelLoaded = useCallback(function (image, channelIndex, isInitialLoad) {
216
244
  // TODO this was once a search by name - is that still necessary or will the index always be correct?
217
245
  var thisChannelSettings = channelSettings[channelIndex];
@@ -226,13 +254,14 @@ var App = function App(props) {
226
254
  var _initializeLut = initializeLut(image, channelIndex, getCurrentViewerChannelSettings()),
227
255
  ramp = _initializeLut.ramp,
228
256
  controlPoints = _initializeLut.controlPoints;
229
- var dtype = thisChannel.dtype;
257
+ var range = DTYPE_RANGE[thisChannel.dtype];
230
258
  changeChannelSetting(channelIndex, {
231
259
  controlPoints: controlPoints,
232
260
  ramp: controlPointsToRamp(ramp),
233
261
  // set the default range of the transfer function editor to cover the full range of the data type
234
- plotMin: DTYPE_RANGE[dtype].min,
235
- plotMax: DTYPE_RANGE[dtype].max
262
+ plotMin: range.min,
263
+ plotMax: range.max,
264
+ isovalue: range.min + (range.max - range.min) / 2
236
265
  });
237
266
  } else {
238
267
  // This channel has already been initialized, but its LUT was just remapped and we need to update some things
@@ -4,9 +4,9 @@ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r)
4
4
  function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
5
5
  function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
6
6
  function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
7
- import { Button, Checkbox, List } from "antd";
7
+ import { Button, Checkbox, InputNumber, List } from "antd";
8
8
  import React, { useCallback, useState } from "react";
9
- import { ISOSURFACE_OPACITY_SLIDER_MAX } from "../../shared/constants";
9
+ import { DTYPE_RANGE, ISOSURFACE_OPACITY_SLIDER_MAX } from "../../shared/constants";
10
10
  import { colorArrayToObject, colorObjectToArray } from "../../shared/utils/colorRepresentations";
11
11
  import ColorPicker from "../ColorPicker";
12
12
  import SliderRow from "../shared/SliderRow";
@@ -37,20 +37,6 @@ var ChannelsWidgetRow = function ChannelsWidgetRow(props) {
37
37
  isosurfaceEnabled: target.checked
38
38
  });
39
39
  };
40
- var onIsovalueChange = function onIsovalueChange(_ref3) {
41
- var _ref4 = _slicedToArray(_ref3, 1),
42
- newValue = _ref4[0];
43
- return changeSettingForThisChannel({
44
- isovalue: newValue
45
- });
46
- };
47
- var onOpacityChange = function onOpacityChange(_ref5) {
48
- var _ref6 = _slicedToArray(_ref5, 1),
49
- newValue = _ref6[0];
50
- return changeSettingForThisChannel({
51
- opacity: newValue / ISOSURFACE_OPACITY_SLIDER_MAX
52
- });
53
- };
54
40
  var onColorChange = function onColorChange(newRGB, _oldRGB, index) {
55
41
  var color = colorObjectToArray(newRGB);
56
42
  props.changeChannelSetting(index, {
@@ -112,17 +98,49 @@ var ChannelsWidgetRow = function ChannelsWidgetRow(props) {
112
98
  });
113
99
  };
114
100
  var renderSurfaceControls = function renderSurfaceControls() {
101
+ var range = DTYPE_RANGE[props.channelDataForChannel.dtype];
115
102
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(SliderRow, {
116
103
  label: "Isovalue",
117
- max: 255,
104
+ min: range.min,
105
+ max: range.max,
118
106
  start: channelState.isovalue,
119
- onChange: onIsovalueChange,
107
+ onChange: function onChange(_ref3) {
108
+ var _ref4 = _slicedToArray(_ref3, 1),
109
+ isovalue = _ref4[0];
110
+ return changeSettingForThisChannel({
111
+ isovalue: isovalue
112
+ });
113
+ },
120
114
  formatInteger: true
121
- }), /*#__PURE__*/React.createElement(SliderRow, {
115
+ }, /*#__PURE__*/React.createElement(InputNumber, {
116
+ value: channelState.isovalue,
117
+ onChange: function onChange(isovalue) {
118
+ return isovalue !== null && changeSettingForThisChannel({
119
+ isovalue: isovalue
120
+ });
121
+ },
122
+ formatter: function formatter(v) {
123
+ return v === undefined ? "" : Number(v).toFixed(0);
124
+ },
125
+ min: range.min,
126
+ max: range.max,
127
+ size: "small",
128
+ controls: false,
129
+ style: {
130
+ width: "64px",
131
+ marginLeft: "8px"
132
+ }
133
+ })), /*#__PURE__*/React.createElement(SliderRow, {
122
134
  label: "Opacity",
123
135
  max: ISOSURFACE_OPACITY_SLIDER_MAX,
124
136
  start: channelState.opacity * ISOSURFACE_OPACITY_SLIDER_MAX,
125
- onChange: onOpacityChange,
137
+ onChange: function onChange(_ref5) {
138
+ var _ref6 = _slicedToArray(_ref5, 1),
139
+ opacity = _ref6[0];
140
+ return changeSettingForThisChannel({
141
+ opacity: opacity / ISOSURFACE_OPACITY_SLIDER_MAX
142
+ });
143
+ },
126
144
  formatInteger: true
127
145
  }), /*#__PURE__*/React.createElement("div", {
128
146
  className: "button-row"
@@ -1,5 +1,5 @@
1
- import React from "react";
2
1
  import { Checkbox } from "antd";
2
+ import React from "react";
3
3
  import SliderRow from "./shared/SliderRow";
4
4
  import { connectToViewerState } from "./ViewerStateProvider";
5
5
  var GlobalVolumeControls = function GlobalVolumeControls(props) {
@@ -26,7 +26,8 @@ var GlobalVolumeControls = function GlobalVolumeControls(props) {
26
26
  paddingBottom: 22
27
27
  }
28
28
  }, showControls.alphaMaskSlider && createSliderRow("mask cell", maskAlpha, 100, "maskAlpha"), showControls.brightnessSlider && createSliderRow("brightness", brightness, 100, "brightness"), showControls.densitySlider && createSliderRow("density", density, 100, "density"), showControls.levelsSliders && createSliderRow("levels", levels, 255, "levels"), showControls.interpolationControl && /*#__PURE__*/React.createElement(SliderRow, {
29
- label: "interpolate"
29
+ label: "interpolate",
30
+ hideSlider: true
30
31
  }, /*#__PURE__*/React.createElement(Checkbox, {
31
32
  checked: props.interpolationEnabled,
32
33
  onChange: function onChange(_ref) {
@@ -8,15 +8,18 @@ var INTEGER_FORMATTER = {
8
8
 
9
9
  /** A component to ensure a single unified style across the many labeled slider rows in the control panel */
10
10
  var SliderRow = function SliderRow(props) {
11
+ var _props$min;
11
12
  return /*#__PURE__*/React.createElement("div", {
12
13
  className: "viewer-control-row"
13
14
  }, /*#__PURE__*/React.createElement("div", {
14
15
  className: "control-name"
15
16
  }, props.label), /*#__PURE__*/React.createElement("div", {
16
17
  className: "control"
17
- }, props.start === undefined ? props.children : !props.hideSlider && /*#__PURE__*/React.createElement(SmarterSlider, {
18
+ }, props.start !== undefined && !props.hideSlider && /*#__PURE__*/React.createElement("div", {
19
+ className: "control-slider"
20
+ }, /*#__PURE__*/React.createElement(SmarterSlider, {
18
21
  range: {
19
- min: 0,
22
+ min: (_props$min = props.min) !== null && _props$min !== void 0 ? _props$min : 0,
20
23
  max: props.max
21
24
  },
22
25
  start: props.start,
@@ -26,6 +29,8 @@ var SliderRow = function SliderRow(props) {
26
29
  format: props.formatInteger ? INTEGER_FORMATTER : undefined,
27
30
  onUpdate: props.onUpdate,
28
31
  onChange: props.onChange
29
- })));
32
+ })), props.children && /*#__PURE__*/React.createElement("div", {
33
+ className: "control-extra"
34
+ }, props.children)));
30
35
  };
31
36
  export default SliderRow;
@@ -16,4 +16,10 @@
16
16
 
17
17
  .viewer-control-row .control{
18
18
  flex:5;
19
+ display:flex;
20
+ align-items:center;
19
21
  }
22
+
23
+ .viewer-control-row .control .control-slider{
24
+ flex-grow:1;
25
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aics/vole-app",
3
- "version": "2.14.0",
3
+ "version": "2.15.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": "^4.1.0",
44
+ "@aics/vole-core": "^4.3.1",
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",
@@ -1,11 +1,12 @@
1
- import React from "react";
2
1
  import { NouisliderProps } from "nouislider-react";
2
+ import React from "react";
3
3
  import "./styles.css";
4
4
  type SliderRowProps = {
5
5
  label: React.ReactNode;
6
6
  start?: number | number[];
7
7
  step?: number;
8
8
  formatInteger?: boolean;
9
+ min?: number;
9
10
  max?: number;
10
11
  onUpdate?: NouisliderProps["onUpdate"];
11
12
  onChange?: NouisliderProps["onChange"];