cloudmr-ux 4.3.3 → 4.3.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.
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ export interface NiivueRoiHistogramProps {
3
+ /** DOM id used by `resampleNiivueRoiHistogram` / `document.getElementById`. Default: `histoplot`. */
4
+ plotElementId?: string;
5
+ className?: string;
6
+ style?: React.CSSProperties;
7
+ }
8
+ /**
9
+ * Mount point for the Plotly ROI histogram. Default id `histoplot` matches MROptimum WebGUI.
10
+ */
11
+ export declare function NiivueRoiHistogram(props: NiivueRoiHistogramProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * Mount point for the Plotly ROI histogram. Default id `histoplot` matches MROptimum WebGUI.
4
+ */
5
+ export function NiivueRoiHistogram(props) {
6
+ var _a;
7
+ var id = (_a = props.plotElementId) !== null && _a !== void 0 ? _a : "histoplot";
8
+ return (_jsx("div", { id: id, className: props.className, style: props.style }));
9
+ }
@@ -0,0 +1,29 @@
1
+ import type { Layout } from "plotly.js-dist-min";
2
+ /** Maps Niivue label index (e.g. "1".."7") to a display name in the legend / ROI table. */
3
+ export type RoiLabelMapping = Record<string, string>;
4
+ export interface NiivueRoiHistogramRow {
5
+ label: string;
6
+ alias: string;
7
+ visibility: boolean;
8
+ color: string;
9
+ mu: number;
10
+ std: number;
11
+ opacity: number;
12
+ count: number;
13
+ sample: number[];
14
+ }
15
+ /** Plotly layout matching MROptimum WebGUI ROI histogram defaults. */
16
+ export declare function getDefaultRoiHistogramLayout(): Partial<Layout>;
17
+ /**
18
+ * Recomputes per-ROI voxel samples from the Niivue instance, updates the Plotly histogram
19
+ * at `plotRoot`, and returns the row data for tables / export.
20
+ *
21
+ * @returns `null` if `plotRoot` is missing (caller should retain prior ROI state), otherwise
22
+ * the new ROI rows (empty when drawing was cleared).
23
+ */
24
+ export declare function resampleNiivueRoiHistogram(options: {
25
+ nv: any;
26
+ labelMapping?: RoiLabelMapping;
27
+ plotRoot: HTMLElement | null | undefined;
28
+ layout?: Partial<Layout>;
29
+ }): NiivueRoiHistogramRow[] | null;
@@ -0,0 +1,134 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import Plotly from "plotly.js-dist-min";
13
+ import { calculateMean, calculateStandardDeviation } from "./roiHistogramStats";
14
+ var ROI_HISTOGRAM_COLORS = [
15
+ "#bbb",
16
+ "#f00",
17
+ "#0f0",
18
+ "#00f",
19
+ "yellow",
20
+ "cyan",
21
+ "#e81ce8",
22
+ "#e8dbc7",
23
+ ];
24
+ /** Plotly layout matching MROptimum WebGUI ROI histogram defaults. */
25
+ export function getDefaultRoiHistogramLayout() {
26
+ return {
27
+ barmode: "overlay",
28
+ title: { text: "ROI Histogram" },
29
+ margin: {
30
+ l: 50,
31
+ r: 50,
32
+ b: 50,
33
+ t: 60,
34
+ pad: 4
35
+ },
36
+ xaxis: {
37
+ title: { text: "Voxel value" },
38
+ showgrid: true
39
+ },
40
+ yaxis: {
41
+ title: { text: "Bin frequency" },
42
+ showgrid: true
43
+ }
44
+ };
45
+ }
46
+ /**
47
+ * Recomputes per-ROI voxel samples from the Niivue instance, updates the Plotly histogram
48
+ * at `plotRoot`, and returns the row data for tables / export.
49
+ *
50
+ * @returns `null` if `plotRoot` is missing (caller should retain prior ROI state), otherwise
51
+ * the new ROI rows (empty when drawing was cleared).
52
+ */
53
+ export function resampleNiivueRoiHistogram(options) {
54
+ var _a, _b, _c, _d;
55
+ var nv = options.nv, _e = options.labelMapping, labelMapping = _e === void 0 ? {} : _e, plotRoot = options.plotRoot, layoutOverrides = options.layout;
56
+ if (typeof document === "undefined" || !plotRoot) {
57
+ return null;
58
+ }
59
+ var image = (_a = nv.volumes) === null || _a === void 0 ? void 0 : _a[0];
60
+ var layout = __assign(__assign({}, getDefaultRoiHistogramLayout()), layoutOverrides);
61
+ if (nv.drawBitmap == null) {
62
+ Plotly.newPlot(plotRoot, [], layout, { responsive: true });
63
+ return [];
64
+ }
65
+ if (!image) {
66
+ return null;
67
+ }
68
+ var min = image.robust_min;
69
+ var max = image.robust_max;
70
+ var samples = {
71
+ 1: [],
72
+ 2: [],
73
+ 3: [],
74
+ 4: [],
75
+ 5: [],
76
+ 6: [],
77
+ 7: []
78
+ };
79
+ for (var i = 0; i < nv.drawBitmap.length; i++) {
80
+ var k = nv.drawBitmap[i];
81
+ if (samples[k] === undefined) {
82
+ samples[k] = [];
83
+ }
84
+ samples[k].push(image.img[i]);
85
+ }
86
+ if (nv.hiddenBitmap !== undefined) {
87
+ for (var i = 0; i < nv.hiddenBitmap.length; i++) {
88
+ var k = nv.hiddenBitmap[i];
89
+ if (samples[k] === undefined) {
90
+ samples[k] = [];
91
+ }
92
+ samples[k].push(image.img[i]);
93
+ }
94
+ }
95
+ var rois = [];
96
+ for (var sk in samples) {
97
+ var key = Number(sk);
98
+ var sample = samples[key];
99
+ if (sample.length > 0 && key > 0) {
100
+ rois.push({
101
+ label: String(key),
102
+ alias: (_c = (_b = labelMapping[sk]) !== null && _b !== void 0 ? _b : labelMapping[String(key)]) !== null && _c !== void 0 ? _c : String(key),
103
+ visibility: nv.getLabelVisibility(Number(key)),
104
+ color: (_d = ROI_HISTOGRAM_COLORS[key]) !== null && _d !== void 0 ? _d : ROI_HISTOGRAM_COLORS[0],
105
+ mu: calculateMean(sample),
106
+ std: calculateStandardDeviation(sample),
107
+ opacity: nv.drawOpacity,
108
+ count: sample.length,
109
+ sample: sample
110
+ });
111
+ }
112
+ }
113
+ var traces = [];
114
+ for (var _i = 0, rois_1 = rois; _i < rois_1.length; _i++) {
115
+ var roi = rois_1[_i];
116
+ traces.push({
117
+ x: roi.sample,
118
+ type: "histogram",
119
+ name: roi.alias,
120
+ opacity: roi.visibility ? 0.5 : 0.1,
121
+ marker: {
122
+ color: roi.color
123
+ },
124
+ autobinx: false,
125
+ xbins: {
126
+ start: min,
127
+ end: max,
128
+ size: (max - min) / 100
129
+ }
130
+ });
131
+ }
132
+ Plotly.newPlot(plotRoot, traces, layout, { responsive: true });
133
+ return rois;
134
+ }
@@ -0,0 +1,2 @@
1
+ export declare function calculateMean(numbers: number[]): number;
2
+ export declare function calculateStandardDeviation(numbers: number[]): number;
@@ -0,0 +1,13 @@
1
+ export function calculateMean(numbers) {
2
+ var sum = numbers.reduce(function (acc, val) { return acc + val; }, 0);
3
+ return sum / numbers.length;
4
+ }
5
+ export function calculateStandardDeviation(numbers) {
6
+ var mean = calculateMean(numbers);
7
+ var squareDiffs = numbers.map(function (value) {
8
+ var diff = value - mean;
9
+ return diff * diff;
10
+ });
11
+ var avgSquareDiff = calculateMean(squareDiffs);
12
+ return Math.sqrt(avgSquareDiff);
13
+ }
@@ -0,0 +1,14 @@
1
+ /// <reference types="react" />
2
+ import type { Layout } from "plotly.js-dist-min";
3
+ import { type NiivueRoiHistogramRow, type RoiLabelMapping } from "./resampleNiivueRoiHistogram";
4
+ export declare function useNiivueRoiHistogram(nv: any, options?: {
5
+ plotElementId?: string;
6
+ /** Optional Plotly layout overrides passed through to each resample. */
7
+ layout?: Partial<Layout>;
8
+ }): {
9
+ rois: NiivueRoiHistogramRow[];
10
+ setRois: import("react").Dispatch<import("react").SetStateAction<NiivueRoiHistogramRow[]>>;
11
+ labelMapping: RoiLabelMapping;
12
+ setLabelMapping: import("react").Dispatch<import("react").SetStateAction<RoiLabelMapping>>;
13
+ resample: (mapping?: RoiLabelMapping) => void;
14
+ };
@@ -0,0 +1,31 @@
1
+ import { useCallback, useState } from "react";
2
+ import { resampleNiivueRoiHistogram, } from "./resampleNiivueRoiHistogram";
3
+ export function useNiivueRoiHistogram(nv, options) {
4
+ var _a;
5
+ var plotElementId = (_a = options === null || options === void 0 ? void 0 : options.plotElementId) !== null && _a !== void 0 ? _a : "histoplot";
6
+ var layout = options === null || options === void 0 ? void 0 : options.layout;
7
+ var _b = useState({}), labelMapping = _b[0], setLabelMapping = _b[1];
8
+ var _c = useState([]), rois = _c[0], setRois = _c[1];
9
+ var resample = useCallback(function (mapping) {
10
+ if (mapping === void 0) { mapping = labelMapping; }
11
+ var el = typeof document !== "undefined"
12
+ ? document.getElementById(plotElementId)
13
+ : null;
14
+ var next = resampleNiivueRoiHistogram({
15
+ nv: nv,
16
+ labelMapping: mapping,
17
+ plotRoot: el,
18
+ layout: layout
19
+ });
20
+ if (next !== null) {
21
+ setRois(next);
22
+ }
23
+ }, [nv, labelMapping, plotElementId, layout]);
24
+ return {
25
+ rois: rois,
26
+ setRois: setRois,
27
+ labelMapping: labelMapping,
28
+ setLabelMapping: setLabelMapping,
29
+ resample: resample
30
+ };
31
+ }
package/dist/index.d.ts CHANGED
@@ -27,6 +27,10 @@ export { NiivueContrastAdjustments } from "./CmrComponents/niivue-contrast-adjus
27
27
  export type { NiivueContrastAdjustmentsProps } from "./CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments";
28
28
  export { DrawToolkit } from "./CmrComponents/draw-toolkit/DrawToolkit";
29
29
  export type { DrawToolkitProps } from "./CmrComponents/draw-toolkit/DrawToolkit";
30
+ export { resampleNiivueRoiHistogram, getDefaultRoiHistogramLayout, } from "./CmrComponents/niivue-roi-histogram/resampleNiivueRoiHistogram";
31
+ export type { NiivueRoiHistogramRow, RoiLabelMapping, } from "./CmrComponents/niivue-roi-histogram/resampleNiivueRoiHistogram";
32
+ export { NiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/NiivueRoiHistogram";
33
+ export { useNiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/useNiivueRoiHistogram";
30
34
  import type { FC } from "react";
31
35
  import type { CmrTableProps } from "./CmrTable/CmrTable";
32
36
  export declare const CmrTable: FC<CmrTableProps>;
package/dist/index.js CHANGED
@@ -25,6 +25,9 @@ export { InvertibleDualSlider } from "./CmrComponents/double-slider/InvertibleDu
25
25
  export { NiivueSlicePosition } from "./CmrComponents/niivue-slice-position/NiivueSlicePosition";
26
26
  export { NiivueContrastAdjustments } from "./CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments";
27
27
  export { DrawToolkit } from "./CmrComponents/draw-toolkit/DrawToolkit";
28
+ export { resampleNiivueRoiHistogram, getDefaultRoiHistogramLayout, } from "./CmrComponents/niivue-roi-histogram/resampleNiivueRoiHistogram";
29
+ export { NiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/NiivueRoiHistogram";
30
+ export { useNiivueRoiHistogram } from "./CmrComponents/niivue-roi-histogram/useNiivueRoiHistogram";
28
31
  import CmrTableComponent from "./CmrTable/CmrTable";
29
32
  export var CmrTable = CmrTableComponent;
30
33
  export * from "./core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.3.3",
3
+ "version": "4.3.5",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",