cloudmr-ux 4.3.1 → 4.3.3

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,10 @@
1
+ import React from "react";
2
+ interface DrawPlatteProps {
3
+ expandDrawOptions: boolean;
4
+ updateDrawPen: (e: any) => void;
5
+ setDrawingEnabled: (enabled: boolean) => void;
6
+ brushSize: number;
7
+ updateBrushSize: (size: number) => void;
8
+ }
9
+ declare const DrawPlatte: React.FC<DrawPlatteProps>;
10
+ export default DrawPlatte;
@@ -0,0 +1,62 @@
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 { Stack, IconButton, Slider, Typography } from "@mui/material";
14
+ import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
15
+ var DrawPlatte = function (_a) {
16
+ var expandDrawOptions = _a.expandDrawOptions, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, brushSize = _a.brushSize, updateBrushSize = _a.updateBrushSize;
17
+ var filledOptions = [
18
+ _jsx(FiberManualRecordIcon, { sx: { color: "red" } }, "f0"),
19
+ _jsx(FiberManualRecordIcon, { sx: { color: "green" } }, "f1"),
20
+ _jsx(FiberManualRecordIcon, { sx: { color: "blue" } }, "f2"),
21
+ _jsx(FiberManualRecordIcon, { sx: { color: "yellow" } }, "f3"),
22
+ _jsx(FiberManualRecordIcon, { sx: { color: "cyan" } }, "f4"),
23
+ _jsx(FiberManualRecordIcon, { sx: { color: "#e81ce8" } }, "f5"),
24
+ ];
25
+ return (_jsxs(Stack, __assign({ style: {
26
+ position: "absolute",
27
+ top: "100%",
28
+ left: 0,
29
+ zIndex: 10,
30
+ border: "".concat(expandDrawOptions ? "1px" : 0, " solid #bbb"),
31
+ maxWidth: expandDrawOptions ? 300 : 0,
32
+ overflow: "hidden",
33
+ borderRadius: "16px",
34
+ borderTopLeftRadius: "6pt",
35
+ borderTopRightRadius: "6pt",
36
+ background: "#333"
37
+ }, direction: "column" }, { children: [_jsxs(Stack, __assign({ sx: { mb: 1 }, alignItems: "center" }, { children: [_jsx(Typography, __assign({ color: "white", noWrap: true, gutterBottom: true, width: "100%", marginLeft: "10pt", fontSize: "11pt", alignItems: "start" }, { children: "Brush Size: ".concat(brushSize) })), _jsx(Slider, { value: brushSize, sx: {
38
+ width: "80%",
39
+ color: "#fff",
40
+ "& .MuiSlider-track": { backgroundColor: "#fff", border: "none" },
41
+ "& .MuiSlider-rail": { backgroundColor: "rgba(255,255,255,0.3)" },
42
+ "& .MuiSlider-thumb": {
43
+ backgroundColor: "#fff",
44
+ border: "2px solid #fff",
45
+ "&:hover, &.Mui-focusVisible, &.Mui-active": {
46
+ boxShadow: "0 0 0 8px rgba(255,255,255,0.16)"
47
+ }
48
+ },
49
+ "& .MuiSlider-mark": { backgroundColor: "#fff" },
50
+ "& .MuiSlider-markActive": { backgroundColor: "#fff" },
51
+ "& .MuiSlider-valueLabel": {
52
+ backgroundColor: "#fff",
53
+ color: "#000"
54
+ }
55
+ }, step: 2, min: 1, max: 15, marks: true, onChange: function (_event, value) {
56
+ updateBrushSize(value);
57
+ } })] })), _jsx(Stack, __assign({ direction: "row" }, { children: filledOptions.map(function (value, index) { return (_jsx(IconButton, __assign({ onClick: function () {
58
+ updateDrawPen({ target: { value: index + 1 } });
59
+ setDrawingEnabled(true);
60
+ } }, { children: value }), index)); }) }))] })));
61
+ };
62
+ export default DrawPlatte;
@@ -0,0 +1,32 @@
1
+ import { CSSProperties } from "react";
2
+ import type { ROI } from "../../core/features/rois/roiTypes";
3
+ export interface DrawToolkitProps {
4
+ nv: any;
5
+ volumes: {
6
+ url: string;
7
+ name: string;
8
+ }[];
9
+ selectedVolume: number;
10
+ updateDrawPen: (e: any) => void;
11
+ drawPen: number;
12
+ setDrawingEnabled: (enabled: boolean) => void;
13
+ drawingEnabled: boolean;
14
+ rois: ROI[];
15
+ selectedROI: number;
16
+ setSelectedROI: (selected: number) => void;
17
+ saveROI: () => void;
18
+ changesMade: boolean;
19
+ drawUndo: () => void;
20
+ style: CSSProperties;
21
+ brushSize: number;
22
+ updateBrushSize: (size: number) => void;
23
+ resampleImage: () => void;
24
+ roiVisible: boolean;
25
+ toggleROIVisible: () => void;
26
+ drawingOpacity: number;
27
+ setDrawingOpacity: (opacity: number) => void;
28
+ labelsVisible: boolean;
29
+ toggleLabelsVisible: () => void;
30
+ setDrawingChanged: (changed: boolean) => void;
31
+ }
32
+ export declare const DrawToolkit: (props: DrawToolkitProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,136 @@
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 { Box, FormControl, IconButton, Slider, Stack, Tooltip, Typography, FormLabel, } from "@mui/material";
14
+ import BrushIcon from "@mui/icons-material/Brush";
15
+ import AutoFixNormalOutlinedIcon from "@mui/icons-material/AutoFixNormalOutlined";
16
+ import ReplyIcon from "@mui/icons-material/Reply";
17
+ import CameraAltIcon from "@mui/icons-material/CameraAlt";
18
+ import React, { useState } from "react";
19
+ import SvgIcon from "@mui/material/SvgIcon";
20
+ import FormatColorFillIcon from "@mui/icons-material/FormatColorFill";
21
+ import DrawPlatte from "./DrawPlatte";
22
+ import DeleteIcon from "@mui/icons-material/Delete";
23
+ import EraserPlatte from "./EraserPlatte";
24
+ import VisibilityIcon from "@mui/icons-material/Visibility";
25
+ import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
26
+ import OpacityIcon from "@mui/icons-material/Opacity";
27
+ import MaskPlatte from "./MaskPlatte";
28
+ import ClickAwayListener from "@mui/material/ClickAwayListener";
29
+ export var DrawToolkit = function (props) {
30
+ var _a = useState("n"), expandedOption = _a[0], setExpandedOption = _a[1];
31
+ var _b = React.useState(false), expandOpacityOptions = _b[0], setExpandOpacityOptions = _b[1];
32
+ var penColor = ["red", "green", "blue", "yellow", "cyan", "#e81ce8"][(props.drawPen & 7) - 1];
33
+ var filled = props.drawPen > 7;
34
+ function clickPaintBrush() {
35
+ if (expandedOption === "d") {
36
+ setExpandedOption("n");
37
+ }
38
+ else {
39
+ setExpandedOption("d");
40
+ }
41
+ props.setDrawingEnabled(expandedOption !== "d");
42
+ }
43
+ function clickEraser() {
44
+ if (expandedOption === "e") {
45
+ setExpandedOption("n");
46
+ }
47
+ else {
48
+ props.updateDrawPen({ target: { value: 8 } });
49
+ setExpandedOption("e");
50
+ }
51
+ props.setDrawingEnabled(expandedOption !== "e");
52
+ }
53
+ var _c = useState(undefined), maskColor = _c[0], setMaskColor = _c[1];
54
+ function clickMask() {
55
+ if (expandedOption === "m") {
56
+ setExpandedOption("n");
57
+ }
58
+ else {
59
+ setExpandedOption("m");
60
+ }
61
+ props.setDrawingEnabled(false);
62
+ }
63
+ return (_jsx(ClickAwayListener, __assign({ onClickAway: function () {
64
+ setExpandedOption("n");
65
+ setExpandOpacityOptions(false);
66
+ props.setDrawingEnabled(false);
67
+ } }, { children: _jsxs(Box, __assign({ sx: {
68
+ display: "flex",
69
+ width: "100%",
70
+ flexDirection: "row",
71
+ justifyItems: "center",
72
+ alignItems: "center",
73
+ borderRadius: "4px",
74
+ height: "20pt",
75
+ backgroundColor: "#333"
76
+ }, style: props.style }, { children: [_jsx(FormControl, { children: _jsx(FormLabel, __assign({ component: "legend", className: "ms-2", style: {
77
+ width: "100%",
78
+ textAlign: "center",
79
+ color: "white",
80
+ fontSize: 16,
81
+ fontWeight: 400
82
+ }, sx: {
83
+ marginBottom: 0,
84
+ marginLeft: 2,
85
+ color: "white",
86
+ fontWeight: 500,
87
+ fontSize: 14
88
+ } }, { children: "ROI Tools:" })) }), _jsx(FormControl, { children: _jsxs(Stack, __assign({ direction: "row" }, { children: [_jsx(IconButton, __assign({ "aria-label": "draw", onClick: clickPaintBrush }, { children: _jsx(BrushIcon, { style: {
89
+ color: expandedOption === "d" && penColor !== undefined
90
+ ? penColor
91
+ : "white"
92
+ } }) })), _jsx(DrawPlatte, { expandDrawOptions: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })) }), _jsx(FormControl, { children: _jsxs(Stack, __assign({ direction: "row" }, { children: [_jsx(IconButton, __assign({ "aria-label": "erase", onClick: clickEraser }, { children: filled || !(expandedOption === "e") ? (_jsx(EraserIcon, { style: { color: "#ddd" } })) : (_jsx(AutoFixNormalOutlinedIcon, { style: { color: "white" } })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })) }), _jsx(FormControl, { children: _jsx(Stack, __assign({ direction: "row" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", onClick: function () {
93
+ props.drawUndo();
94
+ } }, { children: _jsx(ReplyIcon, { style: { color: "white" } }) })) })) }), _jsx(FormControl, { children: _jsx(IconButton, __assign({ "aria-label": "capture", onClick: function () {
95
+ props.nv.saveScene("".concat(props.volumes[props.selectedVolume].name, "_drawing.png"));
96
+ } }, { children: _jsx(CameraAltIcon, { style: { color: "white" } }) })) }), _jsx(FormControl, { children: _jsx(IconButton, __assign({ "aria-label": "delete", onClick: function () {
97
+ props.nv.clearDrawing();
98
+ props.resampleImage();
99
+ props.setDrawingChanged(false);
100
+ } }, { children: _jsx(DeleteIcon, { style: { color: "white" } }) })) }), _jsx(Tooltip, __assign({ title: "Region of interests visibility" }, { children: _jsx(FormControl, { children: _jsx(IconButton, __assign({ "aria-label": "visible", onClick: function () {
101
+ props.toggleROIVisible();
102
+ } }, { children: props.roiVisible ? (_jsx(VisibilityIcon, { style: { color: "white" } })) : (_jsx(VisibilityOffIcon, { style: { color: "white" } })) })) }) })), _jsxs(FormControl, __assign({ style: { flexDirection: "row", alignItems: "center", color: "white" } }, { children: [_jsx(Tooltip, __assign({ title: "Region of interests opacity" }, { children: _jsx(IconButton, __assign({ "aria-label": "opaque", onClick: function () {
103
+ setExpandOpacityOptions(!expandOpacityOptions);
104
+ } }, { children: _jsx(OpacityIcon, { style: { color: "white" } }) })) })), "=", " ".concat(props.drawingOpacity), _jsx(OpacityPlatte, { drawingOpacity: props.drawingOpacity, setDrawingOpacity: props.setDrawingOpacity, expanded: expandOpacityOptions })] })), _jsxs(FormControl, { children: [_jsx(Stack, __assign({ direction: "row" }, { children: _jsx(IconButton, __assign({ "aria-label": "fill", onClick: clickMask }, { children: _jsx(FormatColorFillIcon, { style: {
105
+ color: expandedOption === "m" && maskColor !== undefined
106
+ ? maskColor
107
+ : "white"
108
+ } }) })) })), _jsx(MaskPlatte, { resampleImage: function () {
109
+ props.resampleImage();
110
+ props.setDrawingChanged(true);
111
+ }, expanded: expandedOption === "m", nv: props.nv, setMaskColor: setMaskColor, unfocus: function () {
112
+ setExpandedOption("n");
113
+ } })] })] })) })));
114
+ };
115
+ function EraserIcon(props) {
116
+ return (_jsxs(SvgIcon, __assign({}, props, { viewBox: "0 0 25 25" }, { children: [_jsx("rect", { x: "6", y: "3", width: "12", height: "22", rx: "2", ry: "2", transform: "rotate(230 12 12)", fill: "currentColor" }), _jsx("rect", { x: "7", y: "4", width: "10", height: "8", rx: "2", ry: "2", transform: "rotate(230 12 12)", fill: "#FFFFFF" })] })));
117
+ }
118
+ var OpacityPlatte = function (_a) {
119
+ var drawingOpacity = _a.drawingOpacity, setDrawingOpacity = _a.setDrawingOpacity, expanded = _a.expanded;
120
+ return (_jsx(Stack, __assign({ style: {
121
+ position: "absolute",
122
+ top: "100%",
123
+ left: 0,
124
+ zIndex: 10,
125
+ border: "".concat(expanded ? "1px" : 0, " solid #bbb"),
126
+ maxWidth: expanded ? 300 : 0,
127
+ overflow: "hidden",
128
+ borderRadius: "16px",
129
+ borderTopLeftRadius: "6pt",
130
+ borderTopRightRadius: "6pt",
131
+ background: "#333",
132
+ width: 150
133
+ }, direction: "column" }, { children: _jsxs(Stack, __assign({ sx: { mb: 1 }, alignItems: "center" }, { children: [_jsx(Typography, __assign({ color: "white", noWrap: true, gutterBottom: true, width: "100%", marginLeft: "10pt", fontSize: "11pt", alignItems: "start" }, { children: "Opacity: ".concat(drawingOpacity) })), _jsx(Slider, { sx: { width: "80%" }, value: drawingOpacity, step: 0.01, min: 0, max: 1, onChange: function (_event, value) {
134
+ setDrawingOpacity(value);
135
+ } })] })) })));
136
+ };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ interface EraserPlatteProps {
3
+ expandEraseOptions: boolean;
4
+ updateDrawPen: (e: any) => void;
5
+ setDrawingEnabled: (enabled: boolean) => void;
6
+ brushSize: number;
7
+ updateBrushSize: (size: number) => void;
8
+ }
9
+ declare const EraserPlatte: React.FC<EraserPlatteProps>;
10
+ export default EraserPlatte;
@@ -0,0 +1,42 @@
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 { Stack, IconButton, Slider, Typography } from "@mui/material";
14
+ import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
15
+ import FiberManualRecordOutlinedIcon from "@mui/icons-material/FiberManualRecordOutlined";
16
+ var EraserPlatte = function (_a) {
17
+ var expandEraseOptions = _a.expandEraseOptions, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, brushSize = _a.brushSize, updateBrushSize = _a.updateBrushSize;
18
+ var eraseOptions = [
19
+ _jsx(FiberManualRecordIcon, { style: { color: "white" } }, "e0"),
20
+ _jsx(FiberManualRecordOutlinedIcon, { style: { color: "white" } }, "e1"),
21
+ ];
22
+ return (_jsxs(Stack, __assign({ style: {
23
+ position: "absolute",
24
+ top: "100%",
25
+ left: 0,
26
+ zIndex: 10,
27
+ border: "".concat(expandEraseOptions ? "1px" : 0, " solid #bbb"),
28
+ maxWidth: expandEraseOptions ? 300 : 0,
29
+ overflow: "hidden",
30
+ borderRadius: "16px",
31
+ borderTopLeftRadius: "6pt",
32
+ borderTopRightRadius: "6pt",
33
+ background: "#333",
34
+ width: 150
35
+ }, direction: "column" }, { children: [_jsxs(Stack, __assign({ sx: { mb: 1 }, alignItems: "center" }, { children: [_jsx(Typography, __assign({ color: "white", noWrap: true, gutterBottom: true, width: "100%", marginLeft: "10pt", fontSize: "11pt", alignItems: "start" }, { children: "Eraser Size: ".concat(brushSize) })), _jsx(Slider, { sx: { width: "80%" }, value: brushSize, step: 2, min: 1, max: 15, marks: true, onChange: function (_event, value) {
36
+ updateBrushSize(value);
37
+ } })] })), _jsx(Stack, __assign({ direction: "row", style: { justifyContent: "center" } }, { children: eraseOptions.map(function (value, index) { return (_jsx(IconButton, __assign({ onClick: function () {
38
+ updateDrawPen({ target: { value: index === 0 ? 8 : 0 } });
39
+ setDrawingEnabled(true);
40
+ } }, { children: value }), index)); }) }))] })));
41
+ };
42
+ export default EraserPlatte;
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ interface MaskPlatteProps {
3
+ expanded: boolean;
4
+ nv: any;
5
+ setMaskColor: (color: string | undefined) => void;
6
+ resampleImage: () => void;
7
+ unfocus: () => void;
8
+ }
9
+ declare const MaskPlatte: React.FC<MaskPlatteProps>;
10
+ export default MaskPlatte;
@@ -0,0 +1,92 @@
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 { useEffect, useState } from "react";
14
+ import { Stack, IconButton, Typography, Box } from "@mui/material";
15
+ import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
16
+ import CheckIcon from "@mui/icons-material/Check";
17
+ import CloseIcon from "@mui/icons-material/Close";
18
+ import { InvertibleDualSlider } from "../double-slider/InvertibleDualSlider";
19
+ import { CmrCheckbox } from "../CmrCheckbox/CmrCheckbox";
20
+ var MaskPlatte = function (_a) {
21
+ var expanded = _a.expanded, nv = _a.nv, setMaskColor = _a.setMaskColor, resampleImage = _a.resampleImage, unfocus = _a.unfocus;
22
+ var _b = useState(0), colorIndex = _b[0], storeColorIndex = _b[1];
23
+ var _c = useState("red"), maskColor = _c[0], storeMaskColor = _c[1];
24
+ var _d = useState(false), checked = _d[0], setChecked = _d[1];
25
+ var _e = useState(undefined), original = _e[0], setOriginal = _e[1];
26
+ var colors = ["red", "green", "blue", "yellow", "cyan", "#e81ce8"];
27
+ var filledOptions = colors.map(function (color, i) { return (_jsx(FiberManualRecordIcon, { sx: { color: color } }, i)); });
28
+ if (expanded) {
29
+ setMaskColor(maskColor);
30
+ }
31
+ else {
32
+ setMaskColor(undefined);
33
+ }
34
+ var _f = useState(nv.volumes[0] ? nv.volumes[0].vox_min : 0), min = _f[0], setMin = _f[1];
35
+ var _g = useState(nv.volumes[0] ? nv.volumes[0].vox_max : 1), max = _g[0], setMax = _g[1];
36
+ var cancelMask = function () {
37
+ if (original)
38
+ nv.drawBitmap = new Uint8Array(original);
39
+ else
40
+ return;
41
+ nv.refreshDrawing(true);
42
+ resampleImage();
43
+ setOriginal(undefined);
44
+ };
45
+ useEffect(function () {
46
+ if (!expanded) {
47
+ cancelMask();
48
+ }
49
+ else {
50
+ if (colorIndex !== -1)
51
+ nv.fillRange(min, max, colorIndex + 1, checked, original, setOriginal);
52
+ resampleImage();
53
+ }
54
+ }, [expanded]);
55
+ useEffect(function () {
56
+ if (colorIndex !== -1 && expanded)
57
+ nv.fillRange(min, max, colorIndex + 1, checked, original, setOriginal);
58
+ }, [min, max, checked]);
59
+ return (_jsxs(Stack, __assign({ style: {
60
+ position: "absolute",
61
+ top: "100%",
62
+ left: 0,
63
+ zIndex: 10,
64
+ border: "".concat(expanded ? "1px" : 0, " solid #bbb"),
65
+ maxWidth: expanded ? 450 : 0,
66
+ overflow: "hidden",
67
+ borderRadius: "16px",
68
+ borderTopLeftRadius: "6pt",
69
+ borderTopRightRadius: "6pt",
70
+ background: "#333"
71
+ }, direction: "column" }, { children: [_jsx(Stack, __assign({ alignItems: "center" }, { children: _jsx(Typography, __assign({ color: "white", gutterBottom: true, width: "100%", marginLeft: "10pt", fontSize: "11pt", alignItems: "start" }, { children: "Mask range:" })) })), _jsxs(Stack, __assign({ direction: "row", flexDirection: "row", justifyContent: "center" }, { children: [filledOptions.map(function (value, index) { return (_jsx(IconButton, __assign({ onClick: function () {
72
+ storeColorIndex(index);
73
+ storeMaskColor(colors[index]);
74
+ setMaskColor(colors[index]);
75
+ nv.fillRange(min, max, index + 1, checked, original, setOriginal);
76
+ resampleImage();
77
+ } }, { children: value }), index)); }), _jsx(CmrCheckbox, __assign({ style: { color: "white" }, onChange: function (e) {
78
+ e.stopPropagation();
79
+ setChecked(e.target.checked);
80
+ resampleImage();
81
+ } }, { children: "Inverted" }))] })), _jsx(Stack, __assign({ direction: "row", sx: { mb: 1 } }, { children: _jsx(Box, __assign({ width: 400, style: { paddingLeft: "10px", paddingRight: "10px" } }, { children: _jsx(InvertibleDualSlider, { name: "", min: nv.volumes[0] ? nv.volumes[0].vox_min : 0, max: nv.volumes[0] ? nv.volumes[0].vox_max : 1, reverse: checked, setMin: setMin, setMax: setMax, onFinalize: function () {
82
+ resampleImage();
83
+ } }) })) })), _jsxs(Stack, __assign({ direction: "row", flexDirection: "row", justifyContent: "center" }, { children: [_jsx(IconButton, __assign({ onClick: function () {
84
+ setOriginal(undefined);
85
+ nv.drawAddUndoBitmapWithHiddenVoxels();
86
+ unfocus();
87
+ } }, { children: _jsx(CheckIcon, { style: { color: "green" } }) })), _jsx(IconButton, __assign({ onClick: function () {
88
+ cancelMask();
89
+ unfocus();
90
+ } }, { children: _jsx(CloseIcon, { style: { color: "red" } }) }))] }))] })));
91
+ };
92
+ export default MaskPlatte;
@@ -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;
package/dist/index.d.ts CHANGED
@@ -23,6 +23,10 @@ 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";
28
+ export { DrawToolkit } from "./CmrComponents/draw-toolkit/DrawToolkit";
29
+ export type { DrawToolkitProps } from "./CmrComponents/draw-toolkit/DrawToolkit";
26
30
  import type { FC } from "react";
27
31
  import type { CmrTableProps } from "./CmrTable/CmrTable";
28
32
  export declare const CmrTable: FC<CmrTableProps>;
package/dist/index.js CHANGED
@@ -23,6 +23,8 @@ 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";
27
+ export { DrawToolkit } from "./CmrComponents/draw-toolkit/DrawToolkit";
26
28
  import CmrTableComponent from "./CmrTable/CmrTable";
27
29
  export var CmrTable = CmrTableComponent;
28
30
  export * from "./core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.3.1",
3
+ "version": "4.3.3",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",