@aics/vole-app 2.13.4 → 2.13.5

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/README.md CHANGED
@@ -15,7 +15,7 @@ The volume shader itself is a heavily modified version of one that has distant o
15
15
  ## to use
16
16
 
17
17
  - `https://vole.allencell.org/?url=path/to/ZARR`
18
- - for more url parameters, see [`URL_SPEC.md`](documentation\URL_SPEC.md)
18
+ - for more url parameters, see [`URL_SPEC.md`](documentation/URL_SPEC.md)
19
19
 
20
20
  or as React component:
21
21
 
@@ -108,17 +108,17 @@ var sliderHandleSymbol = {
108
108
  context.closePath();
109
109
  }
110
110
  };
111
- function u8ToAbsolute(value, channel) {
112
- return channel.rawMin + value / 255 * (channel.rawMax - channel.rawMin);
111
+ function binToAbsolute(value, histogram) {
112
+ return histogram.getValueFromBinIndex(value);
113
113
  }
114
- function absoluteToU8(value, channel) {
115
- return (value - channel.rawMin) / (channel.rawMax - channel.rawMin) * 255;
114
+ function absoluteToBin(value, histogram) {
115
+ return histogram.findFractionalBinOfValue(value);
116
116
  }
117
- function controlPointToAbsolute(cp, channel) {
117
+ function controlPointToAbsolute(cp, histogram) {
118
118
  // the x value of the control point is in the range [0, 255]
119
119
  // because of the way the histogram is generated
120
120
  // (see LUT_ENTRIES and the fact that we use Uint8Array)
121
- return u8ToAbsolute(cp.x, channel);
121
+ return binToAbsolute(cp.x, histogram);
122
122
  }
123
123
 
124
124
  /** For when all control points are outside the plot's range: just fill the plot with the settings from 1 point */
@@ -293,12 +293,8 @@ var TfEditor = function TfEditor(props) {
293
293
  var lastColorRef = useRef(TFEDITOR_DEFAULT_COLOR);
294
294
  var svgRef = useRef(null); // need access to SVG element to measure mouse position
295
295
 
296
- var _props$channelData = props.channelData,
297
- rawMin = _props$channelData.rawMin,
298
- rawMax = _props$channelData.rawMax,
299
- dtype = _props$channelData.dtype,
300
- histogram = _props$channelData.histogram;
301
- var typeRange = DTYPE_RANGE[dtype];
296
+ var histogram = props.channelData.histogram;
297
+ var typeRange = DTYPE_RANGE[props.channelData.dtype];
302
298
 
303
299
  // d3 scales define the mapping between data and screen space (and do the heavy lifting of generating plot axes)
304
300
  /** `xScale` is in raw intensity range, not U8 range. We use `u8ToAbsolute` and `absoluteToU8` to translate to U8. */
@@ -306,17 +302,11 @@ var TfEditor = function TfEditor(props) {
306
302
  return d3.scaleLinear().domain([plotMin, plotMax]).range([0, innerWidth]);
307
303
  }, [innerWidth, plotMin, plotMax]);
308
304
  var plotMinU8 = useMemo(function () {
309
- return absoluteToU8(plotMin, {
310
- rawMin: rawMin,
311
- rawMax: rawMax
312
- });
313
- }, [plotMin, rawMin, rawMax]);
305
+ return absoluteToBin(plotMin, histogram);
306
+ }, [plotMin, histogram]);
314
307
  var plotMaxU8 = useMemo(function () {
315
- return absoluteToU8(plotMax, {
316
- rawMin: rawMin,
317
- rawMax: rawMax
318
- });
319
- }, [plotMax, rawMin, rawMax]);
308
+ return absoluteToBin(plotMax, histogram);
309
+ }, [plotMax, histogram]);
320
310
  var yScale = useMemo(function () {
321
311
  return d3.scaleLinear().domain([0, 1]).range([innerHeight, 0]);
322
312
  }, [innerHeight]);
@@ -326,7 +316,7 @@ var TfEditor = function TfEditor(props) {
326
316
  x: 0,
327
317
  y: 0
328
318
  };
329
- return [absoluteToU8(xScale.invert(clamp(event.clientX - svgRect.x - TFEDITOR_MARGINS.left, 0, innerWidth)), props.channelData), yScale.invert(clamp(event.clientY - svgRect.y - TFEDITOR_MARGINS.top, 0, innerHeight))];
319
+ return [absoluteToBin(xScale.invert(clamp(event.clientX - svgRect.x - TFEDITOR_MARGINS.left, 0, innerWidth)), histogram), yScale.invert(clamp(event.clientY - svgRect.y - TFEDITOR_MARGINS.top, 0, innerHeight))];
330
320
  };
331
321
  var dragControlPoint = function dragControlPoint(draggedIdx, x, opacity) {
332
322
  var newControlPoints = _toConsumableArray(controlPointsRef.current);
@@ -461,15 +451,12 @@ var TfEditor = function TfEditor(props) {
461
451
  var areaPath = useMemo(function () {
462
452
  var _areaGenerator;
463
453
  var areaGenerator = d3.area().x(function (d) {
464
- return xScale(controlPointToAbsolute(d, {
465
- rawMin: rawMin,
466
- rawMax: rawMax
467
- }));
454
+ return xScale(controlPointToAbsolute(d, histogram));
468
455
  }).y0(function (d) {
469
456
  return yScale(d.opacity);
470
457
  }).y1(innerHeight).curve(d3.curveLinear);
471
458
  return (_areaGenerator = areaGenerator(controlPointsToRender)) !== null && _areaGenerator !== void 0 ? _areaGenerator : undefined;
472
- }, [controlPointsToRender, xScale, yScale, innerHeight, rawMin, rawMax]);
459
+ }, [controlPointsToRender, xScale, yScale, innerHeight, histogram]);
473
460
 
474
461
  /** d3-generated svg data string representing the "basic mode" min/max slider handles */
475
462
  var sliderHandlePath = useMemo(function () {
@@ -529,17 +516,14 @@ var TfEditor = function TfEditor(props) {
529
516
  .data(binLengthsToRender) // bind the histogram bins to this selection
530
517
  .join("rect") // ensure we have exactly as many bound `rect` elements in the DOM as we have histogram bins
531
518
  .attr("class", "bar").attr("width", barWidth).attr("x", function (_len, idx) {
532
- return xScale(u8ToAbsolute(idx + start, {
533
- rawMin: rawMin,
534
- rawMax: rawMax
535
- }));
519
+ return xScale(binToAbsolute(idx + start, histogram));
536
520
  }) // set position and height from data
537
521
  .attr("y", function (len) {
538
522
  return binScale(len);
539
523
  }).attr("height", function (len) {
540
524
  return innerHeight - binScale(len);
541
525
  });
542
- }, [xScale, rawMin, rawMax, histogram, innerWidth, innerHeight, plotMinU8, plotMaxU8]);
526
+ }, [xScale, histogram, innerWidth, innerHeight, plotMinU8, plotMaxU8]);
543
527
  var applyTFGenerator = useCallback(function (generator) {
544
528
  setSelectedPointIdx(null);
545
529
  lastColorRef.current = TFEDITOR_DEFAULT_COLOR;
@@ -574,7 +558,7 @@ var TfEditor = function TfEditor(props) {
574
558
  return /*#__PURE__*/React.createElement("circle", {
575
559
  key: i,
576
560
  className: i === selectedPointIdx ? "selected" : "",
577
- cx: xScale(controlPointToAbsolute(cp, props.channelData)),
561
+ cx: xScale(controlPointToAbsolute(cp, histogram)),
578
562
  cy: yScale(cp.opacity),
579
563
  style: {
580
564
  fill: colorArrayToString(cp.color)
@@ -606,22 +590,22 @@ var TfEditor = function TfEditor(props) {
606
590
  }, "Advanced")), !props.useControlPoints && /*#__PURE__*/React.createElement("div", {
607
591
  className: "tf-editor-control-row ramp-row"
608
592
  }, "Levels min/max", /*#__PURE__*/React.createElement(InputNumber, {
609
- value: u8ToAbsolute(props.ramp[0], props.channelData),
593
+ value: binToAbsolute(props.ramp[0], histogram),
610
594
  onChange: function onChange(v) {
611
- return v !== null && setRamp([absoluteToU8(v, props.channelData), props.ramp[1]]);
595
+ return v !== null && setRamp([absoluteToBin(v, histogram), props.ramp[1]]);
612
596
  },
613
597
  formatter: numberFormatter,
614
598
  min: typeRange.min,
615
- max: Math.min(u8ToAbsolute(props.ramp[1], props.channelData), typeRange.max),
599
+ max: Math.min(binToAbsolute(props.ramp[1], histogram), typeRange.max),
616
600
  size: "small",
617
601
  controls: false
618
602
  }), /*#__PURE__*/React.createElement(InputNumber, {
619
- value: u8ToAbsolute(props.ramp[1], props.channelData),
603
+ value: binToAbsolute(props.ramp[1], histogram),
620
604
  onChange: function onChange(v) {
621
- return v !== null && setRamp([props.ramp[0], absoluteToU8(v, props.channelData)]);
605
+ return v !== null && setRamp([props.ramp[0], absoluteToBin(v, histogram)]);
622
606
  },
623
607
  formatter: numberFormatter,
624
- min: Math.max(typeRange.min, u8ToAbsolute(props.ramp[0], props.channelData)),
608
+ min: Math.max(typeRange.min, binToAbsolute(props.ramp[0], histogram)),
625
609
  max: typeRange.max,
626
610
  size: "small",
627
611
  controls: false
@@ -667,7 +651,7 @@ var TfEditor = function TfEditor(props) {
667
651
  }), controlPointCircles, !props.useControlPoints && /*#__PURE__*/React.createElement("g", {
668
652
  className: "ramp-sliders"
669
653
  }, plotMinU8 <= props.ramp[0] && props.ramp[0] <= plotMaxU8 && /*#__PURE__*/React.createElement("g", {
670
- transform: "translate(".concat(xScale(u8ToAbsolute(props.ramp[0], props.channelData)), ")")
654
+ transform: "translate(".concat(xScale(binToAbsolute(props.ramp[0], histogram)), ")")
671
655
  }, /*#__PURE__*/React.createElement("line", {
672
656
  y1: innerHeight,
673
657
  strokeDasharray: "5,5",
@@ -686,7 +670,7 @@ var TfEditor = function TfEditor(props) {
686
670
  return setDraggedPointIdx(TfEditorRampSliderHandle.Min);
687
671
  }
688
672
  })), plotMinU8 <= props.ramp[1] && props.ramp[1] <= plotMaxU8 && /*#__PURE__*/React.createElement("g", {
689
- transform: "translate(".concat(xScale(u8ToAbsolute(props.ramp[1], props.channelData)), ")")
673
+ transform: "translate(".concat(xScale(binToAbsolute(props.ramp[1], histogram)), ")")
690
674
  }, /*#__PURE__*/React.createElement("line", {
691
675
  y1: innerHeight,
692
676
  strokeDasharray: "5,5",
@@ -738,8 +722,8 @@ var TfEditor = function TfEditor(props) {
738
722
  },
739
723
  onClick: function onClick() {
740
724
  return changeChannelSetting({
741
- plotMin: rawMin,
742
- plotMax: rawMax
725
+ plotMin: props.channelData.rawMin,
726
+ plotMax: props.channelData.rawMax
743
727
  });
744
728
  }
745
729
  }, "Fit to data"), /*#__PURE__*/React.createElement(Button, {
@@ -40,6 +40,10 @@ function parseLutValue(value, histogram) {
40
40
  // percentile
41
41
  var _parsedValue = parseFloat(value.substring(1)) / 100.0;
42
42
  return histogram.findBinOfPercentile(_parsedValue);
43
+ } else if (firstChar === "v") {
44
+ // value
45
+ var _parsedValue2 = parseFloat(value.substring(1));
46
+ return histogram.findFractionalBinOfValue(_parsedValue2);
43
47
  } else {
44
48
  // plain number
45
49
  return parseFloat(value);
@@ -47,21 +51,26 @@ function parseLutValue(value, histogram) {
47
51
  }
48
52
 
49
53
  /**
50
- * Parses a lookup table (LUT) from a `ViewerChannelSetting` object, where the `lut` field is an
51
- * array of two alphanumeric strings.
54
+ * Parses a lookup table (LUT) from a `ViewerChannelSetting` object, where the
55
+ * `lut` field is an array of two alphanumeric strings.
52
56
  *
53
- * @returns a Lut object if the `lut` field is valid; otherwise, returns undefined.
57
+ * @returns a Lut object if the `lut` field is valid; otherwise, returns
58
+ * undefined.
54
59
  *
55
60
  * Min and max values are determined as following:
56
- * - Plain numbers are direct intensity values.
57
- * - `p{n}` represents a percentile, where `n` is a percentile in the [0, 100] range.
61
+ * - Plain numbers are indices of histogram bins, typically in the range [0,
62
+ * 255].
63
+ * - `v{n}` represents a raw intensity value, where `n` is a number.
64
+ * - `p{n}` represents a percentile, where `n` is a percentile in the [0, 100]
65
+ * range.
58
66
  * - `m{n}` represents the median multiplied by `n / 100`.
59
- * - `autoij` in either the min or max fields will use the "auto" algorithm
60
- * from ImageJ to select the min AND max.
67
+ * - `autoij` in either the min or max fields will use the "auto" algorithm from
68
+ * ImageJ to select the min and max.
61
69
  *
62
70
  * @example
63
71
  * ```
64
- * "0:255" // min: intensity 0, max: intensity 255.
72
+ * "0:255" // min: bin 0, max: bin 255.
73
+ * "v100:v150" // min: intensity 100, max: intensity 150.
65
74
  * "p50:p90" // min: 50th percentile, max: 90th percentile.
66
75
  * "m1:p75" // min: median, max: 75th percentile.
67
76
  * "autoij:0" // use Auto-IJ to calculate min and max.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aics/vole-app",
3
- "version": "2.13.4",
3
+ "version": "2.13.5",
4
4
  "description": "web view of cell volume images",
5
5
  "repository": "github:allen-cell-animated/vole-app",
6
6
  "main": "es/index.js",
@@ -4,21 +4,26 @@ export declare function controlPointsToLut(controlPoints: ControlPoint[]): Lut;
4
4
  /** Returns a default lookup table based on a min/max percentile of the current volume's data. */
5
5
  export declare function getDefaultLut(histogram: Histogram): Lut;
6
6
  /**
7
- * Parses a lookup table (LUT) from a `ViewerChannelSetting` object, where the `lut` field is an
8
- * array of two alphanumeric strings.
7
+ * Parses a lookup table (LUT) from a `ViewerChannelSetting` object, where the
8
+ * `lut` field is an array of two alphanumeric strings.
9
9
  *
10
- * @returns a Lut object if the `lut` field is valid; otherwise, returns undefined.
10
+ * @returns a Lut object if the `lut` field is valid; otherwise, returns
11
+ * undefined.
11
12
  *
12
13
  * Min and max values are determined as following:
13
- * - Plain numbers are direct intensity values.
14
- * - `p{n}` represents a percentile, where `n` is a percentile in the [0, 100] range.
14
+ * - Plain numbers are indices of histogram bins, typically in the range [0,
15
+ * 255].
16
+ * - `v{n}` represents a raw intensity value, where `n` is a number.
17
+ * - `p{n}` represents a percentile, where `n` is a percentile in the [0, 100]
18
+ * range.
15
19
  * - `m{n}` represents the median multiplied by `n / 100`.
16
- * - `autoij` in either the min or max fields will use the "auto" algorithm
17
- * from ImageJ to select the min AND max.
20
+ * - `autoij` in either the min or max fields will use the "auto" algorithm from
21
+ * ImageJ to select the min and max.
18
22
  *
19
23
  * @example
20
24
  * ```
21
- * "0:255" // min: intensity 0, max: intensity 255.
25
+ * "0:255" // min: bin 0, max: bin 255.
26
+ * "v100:v150" // min: intensity 100, max: intensity 150.
22
27
  * "p50:p90" // min: 50th percentile, max: 90th percentile.
23
28
  * "m1:p75" // min: median, max: 75th percentile.
24
29
  * "autoij:0" // use Auto-IJ to calculate min and max.
@@ -10,7 +10,8 @@ export interface ViewerChannelSetting {
10
10
  * Min and max values for the intensity lookup table, which maps from raw intensity values
11
11
  * in the volume to opacity and color. Defaults to [0, 255].
12
12
  *
13
- * - Plain numbers are treated as direct intensity values.
13
+ * - Plain numbers are indices of histogram bins, in the range [0, 255].
14
+ * - `v{n}` represents a raw intensity value, where `n` is an integer.
14
15
  * - `p{n}` represents a percentile, where `n` is a percentile in the [0, 100] range.
15
16
  * - `m{n}` represents the median multiplied by `n / 100`.
16
17
  * - `autoij` in either the min or max fields will use the "auto" algorithm