cloudmr-ux 4.3.0 → 4.3.2
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/dist/CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments.d.ts +91 -0
- package/dist/CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments.js +74 -0
- package/dist/CmrComponents/niivue-slice-position/NiivueSlicePosition.d.ts +6 -1
- package/dist/CmrComponents/niivue-slice-position/NiivueSlicePosition.js +26 -25
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface NiivueContrastAdjustmentsProps {
|
|
3
|
+
/** The Niivue instance. */
|
|
4
|
+
nv: any;
|
|
5
|
+
/**
|
|
6
|
+
* Current window minimum in real space (mirrors `volumes[0].cal_min`).
|
|
7
|
+
* Controlled by the parent — updated via `setMin`.
|
|
8
|
+
*/
|
|
9
|
+
min: number;
|
|
10
|
+
/**
|
|
11
|
+
* Current window maximum in real space (mirrors `volumes[0].cal_max`).
|
|
12
|
+
* Controlled by the parent — updated via `setMax`.
|
|
13
|
+
*/
|
|
14
|
+
max: number;
|
|
15
|
+
/** Called with the new real-space minimum when the user moves the low thumb. */
|
|
16
|
+
setMin: (min: number) => void;
|
|
17
|
+
/** Called with the new real-space maximum when the user moves the high thumb. */
|
|
18
|
+
setMax: (max: number) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Scaling factors used to convert between real space and the display (render)
|
|
21
|
+
* space shown in the dual-range input boxes.
|
|
22
|
+
*
|
|
23
|
+
* Transform: `renderValue = a * realValue - a * b`
|
|
24
|
+
* Inverse: `realValue = renderValue / a + b`
|
|
25
|
+
*
|
|
26
|
+
* Pass `{ a: 1, b: 0 }` for a 1-to-1 mapping (no scientific-notation scaling).
|
|
27
|
+
*/
|
|
28
|
+
transformFactors: {
|
|
29
|
+
a: number;
|
|
30
|
+
b: number;
|
|
31
|
+
};
|
|
32
|
+
/** Current gamma value. Controlled by the parent — updated via `setGamma`. */
|
|
33
|
+
gamma: number;
|
|
34
|
+
/**
|
|
35
|
+
* React key forwarded to the gamma slider to force a remount when gamma is
|
|
36
|
+
* externally reset (e.g. from the Toolbar reset button).
|
|
37
|
+
*/
|
|
38
|
+
gammaKey: number;
|
|
39
|
+
/** Called with the new gamma value when the user moves the gamma slider. */
|
|
40
|
+
setGamma: (val: number) => void;
|
|
41
|
+
/**
|
|
42
|
+
* Optional list of layer controls rendered below the gamma slider (e.g.
|
|
43
|
+
* per-layer color-map pickers). Pass an empty array or omit to render nothing.
|
|
44
|
+
*/
|
|
45
|
+
layerList?: React.ReactNode[];
|
|
46
|
+
/**
|
|
47
|
+
* Heading displayed above the card.
|
|
48
|
+
* @default "Contrast Adjustments"
|
|
49
|
+
*/
|
|
50
|
+
title?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Accent color used on the dual-range slider and the gamma slider.
|
|
53
|
+
* @default "#580f8b"
|
|
54
|
+
*/
|
|
55
|
+
accentColor?: string;
|
|
56
|
+
style?: React.CSSProperties;
|
|
57
|
+
className?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* **NiivueContrastAdjustments**
|
|
61
|
+
*
|
|
62
|
+
* A reusable "Contrast Adjustments" control panel for a Niivue viewer. Renders:
|
|
63
|
+
*
|
|
64
|
+
* 1. A dark **title strip** (`.title` class, same visual style as Slice Position).
|
|
65
|
+
* 2. An outlined **Card** + **CardContent** containing:
|
|
66
|
+
* - A `TKDualRange` for window min/max with optional real→render-space transform.
|
|
67
|
+
* - A gamma range slider.
|
|
68
|
+
* - An optional `layerList` slot for per-layer controls.
|
|
69
|
+
*
|
|
70
|
+
* ### Wiring
|
|
71
|
+
*
|
|
72
|
+
* ```tsx
|
|
73
|
+
* <NiivueContrastAdjustments
|
|
74
|
+
* nv={nv}
|
|
75
|
+
* min={min}
|
|
76
|
+
* max={max}
|
|
77
|
+
* setMin={setMin}
|
|
78
|
+
* setMax={setMax}
|
|
79
|
+
* transformFactors={transformFactors}
|
|
80
|
+
* gamma={gamma}
|
|
81
|
+
* gammaKey={gammaKey}
|
|
82
|
+
* setGamma={setGamma}
|
|
83
|
+
* layerList={layerList}
|
|
84
|
+
* />
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* Pass `title=""` to suppress the heading strip, or `title="Window / Level"` to
|
|
88
|
+
* rename it for a different application.
|
|
89
|
+
*/
|
|
90
|
+
export declare function NiivueContrastAdjustments({ nv, min, max, setMin, setMax, transformFactors, gamma, gammaKey, setGamma, layerList, title, accentColor, style, className, }: NiivueContrastAdjustmentsProps): import("react/jsx-runtime").JSX.Element;
|
|
91
|
+
export default NiivueContrastAdjustments;
|
|
@@ -0,0 +1,74 @@
|
|
|
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 { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import { Card, CardContent, Box } from "@mui/material";
|
|
14
|
+
import TKDualRange from "../tk-dualrange/TKDualRange";
|
|
15
|
+
import CmrLabel from "../label/Label";
|
|
16
|
+
// ─── Component ────────────────────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* **NiivueContrastAdjustments**
|
|
19
|
+
*
|
|
20
|
+
* A reusable "Contrast Adjustments" control panel for a Niivue viewer. Renders:
|
|
21
|
+
*
|
|
22
|
+
* 1. A dark **title strip** (`.title` class, same visual style as Slice Position).
|
|
23
|
+
* 2. An outlined **Card** + **CardContent** containing:
|
|
24
|
+
* - A `TKDualRange` for window min/max with optional real→render-space transform.
|
|
25
|
+
* - A gamma range slider.
|
|
26
|
+
* - An optional `layerList` slot for per-layer controls.
|
|
27
|
+
*
|
|
28
|
+
* ### Wiring
|
|
29
|
+
*
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <NiivueContrastAdjustments
|
|
32
|
+
* nv={nv}
|
|
33
|
+
* min={min}
|
|
34
|
+
* max={max}
|
|
35
|
+
* setMin={setMin}
|
|
36
|
+
* setMax={setMax}
|
|
37
|
+
* transformFactors={transformFactors}
|
|
38
|
+
* gamma={gamma}
|
|
39
|
+
* gammaKey={gammaKey}
|
|
40
|
+
* setGamma={setGamma}
|
|
41
|
+
* layerList={layerList}
|
|
42
|
+
* />
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Pass `title=""` to suppress the heading strip, or `title="Window / Level"` to
|
|
46
|
+
* rename it for a different application.
|
|
47
|
+
*/
|
|
48
|
+
export function NiivueContrastAdjustments(_a) {
|
|
49
|
+
var _b, _c, _d, _e;
|
|
50
|
+
var nv = _a.nv, min = _a.min, max = _a.max, setMin = _a.setMin, setMax = _a.setMax, transformFactors = _a.transformFactors, gamma = _a.gamma, gammaKey = _a.gammaKey, setGamma = _a.setGamma, _f = _a.layerList, layerList = _f === void 0 ? [] : _f, _g = _a.title, title = _g === void 0 ? "Contrast Adjustments" : _g, _h = _a.accentColor, accentColor = _h === void 0 ? "#580f8b" : _h, style = _a.style, className = _a.className;
|
|
51
|
+
var a = transformFactors.a, b = transformFactors.b;
|
|
52
|
+
return (_jsxs("div", __assign({ style: style, className: className }, { children: [title !== "" && (_jsx("div", __assign({ className: "title", style: { width: "100%" } }, { children: title }))), _jsx(Card, __assign({ variant: "outlined", sx: { mb: 2, borderTopLeftRadius: 0, borderTopRightRadius: 0 } }, { children: _jsx(CardContent, { children: _jsxs(Box, __assign({ style: { display: "flex", flex: 1, minWidth: "245px", flexDirection: "column" } }, { children: [_jsx(TKDualRange, { name: "Values:", minDomain: (_c = (_b = nv.volumes[0]) === null || _b === void 0 ? void 0 : _b.robust_min) !== null && _c !== void 0 ? _c : 0, maxDomain: (_e = (_d = nv.volumes[0]) === null || _d === void 0 ? void 0 : _d.robust_max) !== null && _e !== void 0 ? _e : 1, valueLow: min, valueHigh: max, onChangeLow: function (newMin) {
|
|
53
|
+
var v = nv.volumes[0];
|
|
54
|
+
if (!v)
|
|
55
|
+
return;
|
|
56
|
+
v.cal_min = newMin;
|
|
57
|
+
nv.refreshLayers(v, 0, nv.volumes.length);
|
|
58
|
+
nv.drawScene();
|
|
59
|
+
setMin(newMin);
|
|
60
|
+
}, onChangeHigh: function (newMax) {
|
|
61
|
+
var v = nv.volumes[0];
|
|
62
|
+
if (!v)
|
|
63
|
+
return;
|
|
64
|
+
v.cal_max = newMax;
|
|
65
|
+
nv.refreshLayers(v, 0, nv.volumes.length);
|
|
66
|
+
nv.drawScene();
|
|
67
|
+
setMax(newMax);
|
|
68
|
+
}, transform: function (x) { return a * x - a * b; }, inverse: function (y) { return y / a + b; }, step: 0.001, precision: 3, accentColor: accentColor }), _jsxs("div", __assign({ style: { marginTop: 20, marginBottom: 15 } }, { children: [_jsxs(CmrLabel, __assign({ style: { display: "block", marginBottom: 6 } }, { children: ["Gamma: ", gamma.toFixed(2)] })), _jsx("input", { id: "gamma", type: "range", min: 0.1, max: 3.0, step: 0.05, value: gamma, onChange: function (e) {
|
|
69
|
+
var val = Number(e.target.value);
|
|
70
|
+
setGamma(val);
|
|
71
|
+
nv.setGamma(val);
|
|
72
|
+
}, style: { width: "100%", accentColor: accentColor } }, gammaKey)] })), layerList.length > 0 && (_jsx(Box, __assign({ style: { height: "100%" } }, { children: layerList })))] })) }) }))] })));
|
|
73
|
+
}
|
|
74
|
+
export default NiivueContrastAdjustments;
|
|
@@ -28,6 +28,11 @@ export interface NiivueSlicePositionProps {
|
|
|
28
28
|
* Comes from Niivue's `onLocationChange` data.
|
|
29
29
|
*/
|
|
30
30
|
mms: number[];
|
|
31
|
+
/**
|
|
32
|
+
* Heading displayed above the sliders.
|
|
33
|
+
* @default "Slice Position"
|
|
34
|
+
*/
|
|
35
|
+
title?: string;
|
|
31
36
|
/**
|
|
32
37
|
* CSS accent color for all three range inputs.
|
|
33
38
|
* @default "#580f8b"
|
|
@@ -63,5 +68,5 @@ export interface NiivueSlicePositionProps {
|
|
|
63
68
|
* <NiivueSlicePosition nv={nv} mins={mins} maxs={maxs} mms={mms} />
|
|
64
69
|
* ```
|
|
65
70
|
*/
|
|
66
|
-
export declare function NiivueSlicePosition({ nv, mins, maxs, mms, accentColor, style, className, }: NiivueSlicePositionProps): import("react/jsx-runtime").JSX.Element;
|
|
71
|
+
export declare function NiivueSlicePosition({ nv, mins, maxs, mms, title, accentColor, style, className, }: NiivueSlicePositionProps): import("react/jsx-runtime").JSX.Element;
|
|
67
72
|
export default NiivueSlicePosition;
|
|
@@ -11,6 +11,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import React from "react";
|
|
14
|
+
import { Card, CardContent } from "@mui/material";
|
|
14
15
|
import CmrLabel from "../label/Label";
|
|
15
16
|
// ─── Helpers (pure, no React) ─────────────────────────────────────────────────
|
|
16
17
|
var safeSpan = function (min, max) { return Math.max(1e-9, max - min); };
|
|
@@ -46,7 +47,7 @@ var round3 = function (v) { return Math.round(v * 1000) / 1000; };
|
|
|
46
47
|
*/
|
|
47
48
|
export function NiivueSlicePosition(_a) {
|
|
48
49
|
var _b, _c, _d, _e, _f;
|
|
49
|
-
var nv = _a.nv, mins = _a.mins, maxs = _a.maxs, mms = _a.mms, _g = _a.accentColor, accentColor =
|
|
50
|
+
var nv = _a.nv, mins = _a.mins, maxs = _a.maxs, mms = _a.mms, _g = _a.title, title = _g === void 0 ? "Slice Position" : _g, _h = _a.accentColor, accentColor = _h === void 0 ? "#580f8b" : _h, style = _a.style, className = _a.className;
|
|
50
51
|
// ── Derive voxel grid from the loaded volume ─────────────────────────────
|
|
51
52
|
var vol = (_b = nv === null || nv === void 0 ? void 0 : nv.volumes) === null || _b === void 0 ? void 0 : _b[0];
|
|
52
53
|
var meta = (_c = vol === null || vol === void 0 ? void 0 : vol.getImageMetadata) === null || _c === void 0 ? void 0 : _c.call(vol);
|
|
@@ -76,7 +77,7 @@ export function NiivueSlicePosition(_a) {
|
|
|
76
77
|
zAtStart = nv.frac2mm([cx, cy, 0.5 / nz])[2];
|
|
77
78
|
zAtEnd = nv.frac2mm([cx, cy, (nz - 0.5) / nz])[2];
|
|
78
79
|
}
|
|
79
|
-
catch (
|
|
80
|
+
catch (_j) {
|
|
80
81
|
var s = safeSpan(mins[2], maxs[2]) / nz;
|
|
81
82
|
zAtStart = mins[2] + 0.5 * s;
|
|
82
83
|
zAtEnd = maxs[2] - 0.5 * s;
|
|
@@ -127,9 +128,9 @@ export function NiivueSlicePosition(_a) {
|
|
|
127
128
|
}
|
|
128
129
|
};
|
|
129
130
|
// ── Local slider state (mirrors mms; synced by useEffect) ────────────────
|
|
130
|
-
var
|
|
131
|
-
var
|
|
132
|
-
var
|
|
131
|
+
var _k = React.useState(round3(mms[0])), xVal = _k[0], setXVal = _k[1];
|
|
132
|
+
var _l = React.useState(round3(mms[1])), yVal = _l[0], setYVal = _l[1];
|
|
133
|
+
var _m = React.useState(round3(mms[2])), zVal = _m[0], setZVal = _m[1];
|
|
133
134
|
// Keep a ref so snapToVoxel can read the *latest* values without stale closure
|
|
134
135
|
var mmsRef = React.useRef([xVal, yVal, zVal]);
|
|
135
136
|
React.useEffect(function () {
|
|
@@ -207,7 +208,7 @@ export function NiivueSlicePosition(_a) {
|
|
|
207
208
|
try {
|
|
208
209
|
fracZ = nv.mm2frac([xVal, yVal, zVal])[2];
|
|
209
210
|
}
|
|
210
|
-
catch (
|
|
211
|
+
catch (_o) {
|
|
211
212
|
fracZ = ratioAxis(zVal, 2);
|
|
212
213
|
}
|
|
213
214
|
zSliceIndex = clamp(Math.round(fracZ * nz - 0.5), 0, nz - 1);
|
|
@@ -231,24 +232,24 @@ export function NiivueSlicePosition(_a) {
|
|
|
231
232
|
accentColor: accentColor
|
|
232
233
|
};
|
|
233
234
|
// ── Render ────────────────────────────────────────────────────────────────
|
|
234
|
-
return (_jsxs("div", __assign({ style: __assign({
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
235
|
+
return (_jsxs("div", __assign({ style: style, className: className }, { children: [title !== "" && (_jsx("div", __assign({ className: "title", style: { width: "100%" } }, { children: title }))), _jsx(Card, __assign({ variant: "outlined", sx: { mb: 2, borderTopLeftRadius: 0, borderTopRightRadius: 0 } }, { children: _jsx(CardContent, { children: _jsxs("div", __assign({ style: { display: "flex", flexDirection: "column" } }, { children: [_jsxs("div", __assign({ style: { marginBottom: 20 } }, { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "X:" }), _jsx("input", { type: "number", value: xVal.toFixed(3), min: sliderMinX, max: sliderMaxX, step: stepX, style: inputStyle, onChange: function (e) {
|
|
236
|
+
var next = Number(e.target.value);
|
|
237
|
+
if (Number.isFinite(next))
|
|
238
|
+
applyX(next);
|
|
239
|
+
}, onBlur: function (e) {
|
|
240
|
+
applyX(clamp(Number(e.target.value), sliderMinX, sliderMaxX));
|
|
241
|
+
} })] })), _jsx("input", { id: "xSlice", type: "range", min: sliderMinX, max: sliderMaxX, step: stepX, value: clamp(xVal, sliderMinX, sliderMaxX), style: sliderStyle, onChange: function (e) { return applyX(Number(e.target.value)); } })] })), _jsxs("div", __assign({ style: { marginBottom: 20 } }, { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "Y:" }), _jsx("input", { type: "number", value: yVal.toFixed(3), min: sliderMinY, max: sliderMaxY, step: stepY, style: inputStyle, onChange: function (e) {
|
|
242
|
+
var next = Number(e.target.value);
|
|
243
|
+
if (Number.isFinite(next))
|
|
244
|
+
applyY(next);
|
|
245
|
+
}, onBlur: function (e) {
|
|
246
|
+
applyY(clamp(Number(e.target.value), sliderMinY, sliderMaxY));
|
|
247
|
+
} })] })), _jsx("input", { id: "ySlice", type: "range", min: sliderMinY, max: sliderMaxY, step: stepY, value: clamp(yVal, sliderMinY, sliderMaxY), style: sliderStyle, onChange: function (e) { return applyY(Number(e.target.value)); } })] })), _jsxs("div", { children: [_jsxs("div", __assign({ style: rowStyle }, { children: [_jsx(CmrLabel, { children: "Z:" }), _jsx("input", { type: "number", value: zVal.toFixed(3), min: sliderMinZ, max: sliderMaxZ, step: stepZ, style: inputStyle, onChange: function (e) {
|
|
248
|
+
var next = Number(e.target.value);
|
|
249
|
+
if (Number.isFinite(next))
|
|
250
|
+
applyZ(next);
|
|
251
|
+
}, onBlur: function (e) {
|
|
252
|
+
applyZ(clamp(Number(e.target.value), sliderMinZ, sliderMaxZ));
|
|
253
|
+
} })] })), _jsx("input", { id: "zSlice", type: "range", min: 0, max: Math.max(0, nz - 1), step: 1, value: zSliceIndex, style: sliderStyle, onChange: function (e) { return applyZBySliceIndex(Number(e.target.value)); } })] })] })) }) }))] })));
|
|
253
254
|
}
|
|
254
255
|
export default NiivueSlicePosition;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export { InvertibleDualSlider } from "./CmrComponents/double-slider/InvertibleDu
|
|
|
23
23
|
export type { LambdaFile } from "./CmrComponents/upload/Upload";
|
|
24
24
|
export { NiivueSlicePosition } from "./CmrComponents/niivue-slice-position/NiivueSlicePosition";
|
|
25
25
|
export type { NiivueSlicePositionProps } from "./CmrComponents/niivue-slice-position/NiivueSlicePosition";
|
|
26
|
+
export { NiivueContrastAdjustments } from "./CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments";
|
|
27
|
+
export type { NiivueContrastAdjustmentsProps } from "./CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments";
|
|
26
28
|
import type { FC } from "react";
|
|
27
29
|
import type { CmrTableProps } from "./CmrTable/CmrTable";
|
|
28
30
|
export declare const CmrTable: FC<CmrTableProps>;
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ export { DualSlider } from "./CmrComponents/double-slider/DualSlider";
|
|
|
23
23
|
export { Slider } from "./CmrComponents/gui-slider/Slider";
|
|
24
24
|
export { InvertibleDualSlider } from "./CmrComponents/double-slider/InvertibleDualSlider";
|
|
25
25
|
export { NiivueSlicePosition } from "./CmrComponents/niivue-slice-position/NiivueSlicePosition";
|
|
26
|
+
export { NiivueContrastAdjustments } from "./CmrComponents/niivue-contrast-adjustments/NiivueContrastAdjustments";
|
|
26
27
|
import CmrTableComponent from "./CmrTable/CmrTable";
|
|
27
28
|
export var CmrTable = CmrTableComponent;
|
|
28
29
|
export * from "./core";
|