cloudmr-ux 4.6.3 → 4.6.6

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.
@@ -186,7 +186,7 @@ export function CloudMrNiivuePanel(props) {
186
186
  left: 0,
187
187
  width: "100%",
188
188
  height: "100%"
189
- } }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange, onApplyDraft: props.onApplyShapeDraft, overlayKey: props.mms })), props.penDraft && (_jsx(PenDraftOverlay, { nv: props.nv, draft: props.penDraft, onDraftChange: props.onPenDraftChange, onApplyDraft: props.onApplyPenDraft, overlayKey: props.mms }))] }))] })), _jsxs(Box, __assign({ sx: {
189
+ } }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange, overlayKey: props.mms })), props.penDraft && (_jsx(PenDraftOverlay, { nv: props.nv, draft: props.penDraft, onDraftChange: props.onPenDraftChange, overlayKey: props.mms }))] }))] })), _jsxs(Box, __assign({ sx: {
190
190
  width: {
191
191
  xs: "100%",
192
192
  md: "35%"
@@ -832,12 +832,6 @@ export default function CloudMrNiivueViewer(props) {
832
832
  (_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
833
833
  }
834
834
  }
835
- function syncActiveShapeDraftRef() {
836
- nv._cloudMrActiveShapeDraft = shapeDraftRef.current;
837
- }
838
- function clearActiveShapeDraftRef() {
839
- nv._cloudMrActiveShapeDraft = null;
840
- }
841
835
  function cancelPenDraftHandler() {
842
836
  var _a;
843
837
  var draft = penDraftRef.current;
@@ -926,7 +920,6 @@ export default function CloudMrNiivueViewer(props) {
926
920
  setShapeDraft(null);
927
921
  shapeDraftRef.current = null;
928
922
  nv._cloudMrShapeDraftActive = false;
929
- clearActiveShapeDraftRef();
930
923
  if (drawShapeToolRef.current) {
931
924
  nvSetDrawingEnabled(true);
932
925
  }
@@ -939,7 +932,6 @@ export default function CloudMrNiivueViewer(props) {
939
932
  setShapeDraft(null);
940
933
  shapeDraftRef.current = null;
941
934
  nv._cloudMrShapeDraftActive = false;
942
- clearActiveShapeDraftRef();
943
935
  if (drawShapeToolRef.current) {
944
936
  nvSetDrawingEnabled(true);
945
937
  }
@@ -948,13 +940,11 @@ export default function CloudMrNiivueViewer(props) {
948
940
  function onShapeDraftChange(draft) {
949
941
  setShapeDraft(draft);
950
942
  shapeDraftRef.current = draft;
951
- syncActiveShapeDraftRef();
952
943
  }
953
944
  nv.onShapeDraftReady = function (draft) {
954
945
  setShapeDraft(draft);
955
946
  shapeDraftRef.current = draft;
956
947
  nv._cloudMrShapeDraftActive = true;
957
- syncActiveShapeDraftRef();
958
948
  nvSetDrawingEnabled(false);
959
949
  };
960
950
  nv.onApplyActiveDraft = function () {
@@ -971,6 +961,32 @@ export default function CloudMrNiivueViewer(props) {
971
961
  React.useEffect(function () {
972
962
  nv._cloudMrShapeDraftActive = shapeDraft != null;
973
963
  }, [shapeDraft]);
964
+ React.useEffect(function () {
965
+ if (!shapeDraft && !penDraft) {
966
+ return undefined;
967
+ }
968
+ var canvas = document.getElementById("niiCanvas");
969
+ if (!canvas) {
970
+ return undefined;
971
+ }
972
+ var applyOnRightClick = function (event) {
973
+ var _a;
974
+ event.preventDefault();
975
+ event.stopPropagation();
976
+ (_a = nv.onApplyActiveDraft) === null || _a === void 0 ? void 0 : _a.call(nv);
977
+ };
978
+ var onMouseDown = function (event) {
979
+ if (event.button === 2) {
980
+ applyOnRightClick(event);
981
+ }
982
+ };
983
+ canvas.addEventListener("mousedown", onMouseDown, true);
984
+ canvas.addEventListener("contextmenu", applyOnRightClick, true);
985
+ return function () {
986
+ canvas.removeEventListener("mousedown", onMouseDown, true);
987
+ canvas.removeEventListener("contextmenu", applyOnRightClick, true);
988
+ };
989
+ }, [shapeDraft, penDraft]);
974
990
  React.useEffect(function () {
975
991
  if (!shapeDraft && !penDraft) {
976
992
  return undefined;
@@ -984,6 +1000,16 @@ export default function CloudMrNiivueViewer(props) {
984
1000
  else if (penDraft) {
985
1001
  cancelPenDraftHandler();
986
1002
  }
1003
+ return;
1004
+ }
1005
+ if (event.key === "Enter") {
1006
+ event.preventDefault();
1007
+ if (shapeDraft) {
1008
+ applyShapeDraft();
1009
+ }
1010
+ else if (penDraft) {
1011
+ applyPenDraftHandler();
1012
+ }
987
1013
  }
988
1014
  };
989
1015
  window.addEventListener("keydown", onKeyDown);
@@ -6,7 +6,6 @@ import {
6
6
  captureDeferredShapeDraft,
7
7
  captureShapeDraftFromClick,
8
8
  isDraftTooSmall,
9
- isVoxelPartOfDraft,
10
9
  redrawDraftShape,
11
10
  shouldDeferShapeCommit,
12
11
  } from "./shapeDraftUtils.js";
@@ -1469,85 +1468,54 @@ Niivue.prototype.cloudMrResetPolyline = function cloudMrResetPolyline() {
1469
1468
  resetPolylineState(this);
1470
1469
  };
1471
1470
 
1471
+ const RIGHT_MOUSE_BUTTON = 2;
1472
+
1472
1473
  function cloudMrHasApplyableDraft(nv) {
1473
1474
  return !!(nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive);
1474
1475
  }
1475
1476
 
1476
- function isClickOnActiveShapeDraft(nv) {
1477
- const draft = nv._cloudMrActiveShapeDraft;
1478
- if (!draft || !nv._cloudMrShapeDraftActive) return false;
1479
- const vox = voxFromMouse(nv);
1480
- if (!vox) return false;
1481
- return isVoxelPartOfDraft(nv, draft, vox);
1482
- }
1483
-
1484
- function canReopenShapeDraftOnClick(nv) {
1485
- const penType = nv.opts.penType;
1486
- return (
1487
- nv.opts.deferShapeCommit &&
1488
- nv.opts.drawingEnabled &&
1489
- !nv._cloudMrShapeDraftActive &&
1490
- !nv._cloudMrPenDraftActive &&
1491
- (penType === NI_PEN_TYPE.RECTANGLE || penType === NI_PEN_TYPE.ELLIPSE)
1492
- );
1493
- }
1494
-
1495
- function cloudMrOpenShapeDraftFromClick(nv) {
1496
- const reopenDraft = captureShapeDraftFromClick(nv);
1497
- if (!reopenDraft) return false;
1498
- redrawDraftShape(nv, reopenDraft);
1499
- nv._cloudMrSuppressDrawingChangedMouseUp = true;
1500
- if (typeof nv.onShapeDraftReady === "function") {
1501
- nv.onShapeDraftReady(reopenDraft);
1477
+ function cloudMrTryApplyDraftOnRightClick(nv, event) {
1478
+ if (!cloudMrHasApplyableDraft(nv)) {
1479
+ return false;
1480
+ }
1481
+ event.preventDefault();
1482
+ event.stopPropagation();
1483
+ if (typeof nv.onApplyActiveDraft === "function") {
1484
+ nv.onApplyActiveDraft();
1502
1485
  }
1503
1486
  return true;
1504
1487
  }
1505
1488
 
1506
1489
  /**
1507
- * Re-enter rectangle/ellipse edit mode when clicking an existing shape ROI.
1490
+ * Re-enter rectangle/ellipse edit mode when clicking an existing applied ROI.
1508
1491
  */
1509
1492
  function cloudMrTryReopenShapeDraftOnClick(nv) {
1510
- if (!isClickWithoutDrag(nv.uiData) || !canReopenShapeDraftOnClick(nv)) {
1511
- return false;
1512
- }
1513
- return cloudMrOpenShapeDraftFromClick(nv);
1514
- }
1515
-
1516
- /**
1517
- * While editing one shape, mousedown on another shape applies the current
1518
- * draft immediately and opens the clicked shape for editing.
1519
- */
1520
- function cloudMrTrySwitchShapeDraftOnMouseDown(nv) {
1521
- if (!nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive) {
1522
- return false;
1523
- }
1524
- if (!nv.opts.deferShapeCommit || !nv.opts.drawingEnabled) {
1525
- return false;
1526
- }
1527
-
1528
- const vox = voxFromMouse(nv);
1529
- if (!vox || isClickOnActiveShapeDraft(nv)) {
1493
+ const penType = nv.opts.penType;
1494
+ if (
1495
+ !nv.opts.deferShapeCommit ||
1496
+ !nv.opts.drawingEnabled ||
1497
+ nv._cloudMrShapeDraftActive ||
1498
+ nv._cloudMrPenDraftActive ||
1499
+ !isClickWithoutDrag(nv.uiData) ||
1500
+ (penType !== NI_PEN_TYPE.RECTANGLE && penType !== NI_PEN_TYPE.ELLIPSE)
1501
+ ) {
1530
1502
  return false;
1531
1503
  }
1532
1504
 
1533
- const dims = nv.back?.dims;
1534
- if (!dims || !nv.drawBitmap) return false;
1535
- const dx = dims[1];
1536
- const dy = dims[2];
1537
- const idx = vox[0] + vox[1] * dx + vox[2] * dx * dy;
1538
- if (!nv.drawBitmap[idx]) {
1539
- return false;
1540
- }
1505
+ const reopenDraft = captureShapeDraftFromClick(nv);
1506
+ if (!reopenDraft) return false;
1541
1507
 
1542
- if (typeof nv.onApplyActiveDraft === "function") {
1543
- nv.onApplyActiveDraft();
1508
+ redrawDraftShape(nv, reopenDraft);
1509
+ nv._cloudMrSuppressDrawingChangedMouseUp = true;
1510
+ if (typeof nv.onShapeDraftReady === "function") {
1511
+ nv.onShapeDraftReady(reopenDraft);
1544
1512
  }
1545
- return cloudMrOpenShapeDraftFromClick(nv);
1513
+ return true;
1546
1514
  }
1547
1515
 
1548
1516
  const _mouseDownListener = Niivue.prototype.mouseDownListener;
1549
1517
  Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
1550
- if (e.button === 0 && cloudMrTrySwitchShapeDraftOnMouseDown(this)) {
1518
+ if (e.button === RIGHT_MOUSE_BUTTON && cloudMrTryApplyDraftOnRightClick(this, e)) {
1551
1519
  return;
1552
1520
  }
1553
1521
  if (shouldDeferFreehandCommit(this) && this.drawBitmap) {
@@ -1,12 +1,10 @@
1
1
  /**
2
2
  * Adjust handles for polyline (vertex drag) or freehand (move only) drafts.
3
- * @param {{ nv: any, draft: any, onDraftChange: (d: any) => void, onApplyDraft?: () => void, overlayKey?: unknown }} props
4
3
  */
5
- export function PenDraftOverlay({ nv, draft, onDraftChange, onApplyDraft, overlayKey }: {
4
+ export function PenDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
6
5
  nv: any;
7
6
  draft: any;
8
- onDraftChange: (d: any) => void;
9
- onApplyDraft?: (() => void) | undefined;
10
- overlayKey?: unknown;
7
+ onDraftChange: any;
8
+ overlayKey: any;
11
9
  }): import("react/jsx-runtime").JSX.Element | null;
12
10
  export default PenDraftOverlay;
@@ -29,10 +29,9 @@ function cloneFreehandDraft(draft) {
29
29
  }
30
30
  /**
31
31
  * Adjust handles for polyline (vertex drag) or freehand (move only) drafts.
32
- * @param {{ nv: any, draft: any, onDraftChange: (d: any) => void, onApplyDraft?: () => void, overlayKey?: unknown }} props
33
32
  */
34
33
  export function PenDraftOverlay(_a) {
35
- var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, onApplyDraft = _a.onApplyDraft, overlayKey = _a.overlayKey;
34
+ var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
36
35
  var dragRef = useRef(null);
37
36
  var draftRef = useRef(draft);
38
37
  draftRef.current = draft;
@@ -110,7 +109,6 @@ export function PenDraftOverlay(_a) {
110
109
  onDraftChange(nextDraft);
111
110
  }, [nv, onDraftChange]);
112
111
  finishDragRef.current = function () {
113
- var hadDrag = dragRef.current != null;
114
112
  dragRef.current = null;
115
113
  if (onPointerMoveRef.current) {
116
114
  window.removeEventListener("pointermove", onPointerMoveRef.current);
@@ -118,10 +116,6 @@ export function PenDraftOverlay(_a) {
118
116
  if (finishDragRef.current) {
119
117
  window.removeEventListener("pointerup", finishDragRef.current);
120
118
  }
121
- // Auto-apply as soon as the user releases the handle after a move/resize
122
- if (hadDrag && onApplyDraft) {
123
- onApplyDraft();
124
- }
125
119
  };
126
120
  onPointerMoveRef.current = function (event) {
127
121
  var drag = dragRef.current;
@@ -1,12 +1,11 @@
1
1
  /**
2
2
  * Overlay handles for adjusting a rectangle/ellipse draft before commit.
3
- * @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, onApplyDraft?: () => void, overlayKey?: unknown }} props
3
+ * @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
4
4
  */
5
- export function ShapeDraftOverlay({ nv, draft, onDraftChange, onApplyDraft, overlayKey }: {
5
+ export function ShapeDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
6
6
  nv: any;
7
7
  draft: import('./shapeDraftUtils').ShapeDraft;
8
8
  onDraftChange: (d: any) => void;
9
- onApplyDraft?: (() => void) | undefined;
10
9
  overlayKey?: unknown;
11
10
  }): import("react/jsx-runtime").JSX.Element | null;
12
11
  export default ShapeDraftOverlay;
@@ -25,11 +25,11 @@ var HANDLE_SIZE = 10;
25
25
  var ACCENT = "#580f8b";
26
26
  /**
27
27
  * Overlay handles for adjusting a rectangle/ellipse draft before commit.
28
- * @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, onApplyDraft?: () => void, overlayKey?: unknown }} props
28
+ * @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
29
29
  */
30
30
  export function ShapeDraftOverlay(_a) {
31
31
  var _b;
32
- var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, onApplyDraft = _a.onApplyDraft, overlayKey = _a.overlayKey;
32
+ var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
33
33
  var dragRef = useRef(null);
34
34
  var draftRef = useRef(draft);
35
35
  draftRef.current = draft;
@@ -84,7 +84,6 @@ export function ShapeDraftOverlay(_a) {
84
84
  onDraftChange(nextDraft);
85
85
  }, [nv, onDraftChange]);
86
86
  finishDragRef.current = function () {
87
- var hadDrag = dragRef.current != null;
88
87
  dragRef.current = null;
89
88
  if (onPointerMoveRef.current) {
90
89
  window.removeEventListener("pointermove", onPointerMoveRef.current);
@@ -92,10 +91,6 @@ export function ShapeDraftOverlay(_a) {
92
91
  if (finishDragRef.current) {
93
92
  window.removeEventListener("pointerup", finishDragRef.current);
94
93
  }
95
- // Auto-apply as soon as the user releases the handle after a move/resize
96
- if (hadDrag && onApplyDraft) {
97
- onApplyDraft();
98
- }
99
94
  };
100
95
  onPointerMoveRef.current = function (event) {
101
96
  var drag = dragRef.current;
@@ -1,4 +1,4 @@
1
- export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onCancelPenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onCancelShapeDraft, }: {
1
+ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onApplyPenDraft, onCancelPenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
2
2
  expanded: any;
3
3
  updateDrawPen: any;
4
4
  setDrawingEnabled: any;
@@ -7,10 +7,12 @@ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEna
7
7
  onPenDrawModeChange: any;
8
8
  polylineVertexCount?: number | undefined;
9
9
  penDraftActive?: boolean | undefined;
10
+ onApplyPenDraft: any;
10
11
  onCancelPenDraft: any;
11
12
  onFillPenDraft: any;
12
13
  brushSize?: number | undefined;
13
14
  updateBrushSize: any;
14
15
  shapeDraftActive?: boolean | undefined;
16
+ onApplyShapeDraft: any;
15
17
  onCancelShapeDraft: any;
16
18
  }): import("react/jsx-runtime").JSX.Element;
@@ -11,13 +11,12 @@ var __assign = (this && this.__assign) || function () {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  /**
14
- * Pen palette adds freehand vs polyline mode; pen/shape drafts show Cancel while adjusting.
15
- * Shapes are applied automatically on mouse release.
14
+ * Pen palette adds freehand vs polyline mode; pen/shape drafts show Apply/Cancel while adjusting.
16
15
  */
17
16
  import { Stack, IconButton, Button, Tooltip, Typography } from "@mui/material";
18
17
  import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
18
+ import CheckIcon from "@mui/icons-material/Check";
19
19
  import CloseIcon from "@mui/icons-material/Close";
20
- import FormatColorFillIcon from "@mui/icons-material/FormatColorFill";
21
20
  import { BrushSizeSlider } from "./BrushSizeSlider";
22
21
  var FILLED_COLORS = [
23
22
  { sx: { color: "red" } },
@@ -38,7 +37,7 @@ var modeBtnSx = function (active) { return ({
38
37
  px: 0.75
39
38
  }); };
40
39
  export default function DrawColorPlatte(_a) {
41
- var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d, _e = _a.penDraftActive, penDraftActive = _e === void 0 ? false : _e, onCancelPenDraft = _a.onCancelPenDraft, onFillPenDraft = _a.onFillPenDraft, _f = _a.brushSize, brushSize = _f === void 0 ? 1 : _f, updateBrushSize = _a.updateBrushSize, _g = _a.shapeDraftActive, shapeDraftActive = _g === void 0 ? false : _g, onCancelShapeDraft = _a.onCancelShapeDraft;
40
+ var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d, _e = _a.penDraftActive, penDraftActive = _e === void 0 ? false : _e, onApplyPenDraft = _a.onApplyPenDraft, onCancelPenDraft = _a.onCancelPenDraft, onFillPenDraft = _a.onFillPenDraft, _f = _a.brushSize, brushSize = _f === void 0 ? 1 : _f, updateBrushSize = _a.updateBrushSize, _g = _a.shapeDraftActive, shapeDraftActive = _g === void 0 ? false : _g, onApplyShapeDraft = _a.onApplyShapeDraft, onCancelShapeDraft = _a.onCancelShapeDraft;
42
41
  return (_jsxs(Stack, __assign({ style: {
43
42
  position: "absolute",
44
43
  top: "100%",
@@ -63,22 +62,36 @@ export default function DrawColorPlatte(_a) {
63
62
  py: 0.25,
64
63
  px: 0.75,
65
64
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
66
- } }, { children: "Cancel" })) })), penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: "Fill interior (keeps outline editable until release)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "fill polyline", onClick: function () { return onFillPenDraft === null || onFillPenDraft === void 0 ? void 0 : onFillPenDraft(); }, startIcon: _jsx(FormatColorFillIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
65
+ } }, { children: "Cancel" })) })), _jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 1 }, { children: [penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: "Fill interior (keeps outline editable until Apply)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "fill polyline", onClick: function () { return onFillPenDraft === null || onFillPenDraft === void 0 ? void 0 : onFillPenDraft(); }, sx: {
66
+ color: "#c9a0e8",
67
+ fontSize: ACTION_FONT_SIZE,
68
+ textTransform: "none",
69
+ minWidth: 0,
70
+ py: 0.25,
71
+ px: 0.75
72
+ } }, { children: "Fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply shape (enter or right-click)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply pen draft", onClick: function () { return onApplyPenDraft === null || onApplyPenDraft === void 0 ? void 0 : onApplyPenDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
73
+ color: "#c9a0e8",
74
+ fontSize: ACTION_FONT_SIZE,
75
+ textTransform: "none",
76
+ minWidth: 0,
77
+ py: 0.25,
78
+ px: 0.75,
79
+ "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
80
+ } }, { children: "Apply" })) }))] }))] }))), shapeDraftActive && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: [_jsx(Tooltip, __assign({ title: "Cancel shape (Esc)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "cancel shape", onClick: function () { return onCancelShapeDraft === null || onCancelShapeDraft === void 0 ? void 0 : onCancelShapeDraft(); }, startIcon: _jsx(CloseIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
81
+ color: "#ccc",
82
+ fontSize: ACTION_FONT_SIZE,
83
+ textTransform: "none",
84
+ minWidth: 0,
85
+ py: 0.25,
86
+ px: 0.75,
87
+ "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
88
+ } }, { children: "Cancel" })) })), _jsx(Tooltip, __assign({ title: "Apply shape (enter or right-click)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply shape", onClick: function () { return onApplyShapeDraft === null || onApplyShapeDraft === void 0 ? void 0 : onApplyShapeDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
67
89
  color: "#c9a0e8",
68
90
  fontSize: ACTION_FONT_SIZE,
69
91
  textTransform: "none",
70
92
  minWidth: 0,
71
93
  py: 0.25,
72
94
  px: 0.75,
73
- ml: "auto",
74
95
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
75
- } }, { children: "Fill" })) })))] }))), shapeDraftActive && expanded && (_jsx(Stack, __assign({ direction: "row", alignItems: "center", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: _jsx(Tooltip, __assign({ title: "Cancel shape (Esc)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "cancel shape", onClick: function () { return onCancelShapeDraft === null || onCancelShapeDraft === void 0 ? void 0 : onCancelShapeDraft(); }, startIcon: _jsx(CloseIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
76
- color: "#ccc",
77
- fontSize: ACTION_FONT_SIZE,
78
- textTransform: "none",
79
- minWidth: 0,
80
- py: 0.25,
81
- px: 0.75,
82
- "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
83
- } }, { children: "Cancel" })) })) })))] })));
96
+ } }, { children: "Apply" })) }))] })))] })));
84
97
  }
@@ -49,27 +49,10 @@ export function captureDeferredShapeDraft(nv: any): {
49
49
  baseBitmap: Uint8Array | null;
50
50
  };
51
51
  export function shouldDeferShapeCommit(nv: any): any;
52
- /**
53
- * Flood-fill a connected voxel cluster from a seed.
54
- * @returns {{ label: number, visited: Set<number>, voxels: [number,number,number][], bounds: object } | null}
55
- */
56
- export function floodFillClusterFromVox(nv: any, seedVox: any): {
57
- label: number;
58
- visited: Set<number>;
59
- voxels: [number, number, number][];
60
- bounds: object;
61
- } | null;
62
- /** True when a voxel belongs to the live draft overlay (drawn on top of baseBitmap). */
63
- export function isVoxelPartOfDraft(nv: any, draft: any, seedVox: any): boolean;
64
- export function eraseClusterFromBitmap(bitmap: any, visited: any): Uint8Array;
65
- /** Guess rectangle vs ellipse from the filled voxel pattern, not the active tool. */
66
- export function inferShapePenTypeFromCluster(cluster: any, axCorSag: any): 1 | 2;
67
52
  /**
68
53
  * When the user clicks on an existing filled ROI while the rectangle/ellipse tool
69
54
  * is active (but no draft is currently open), flood-fill the clicked cluster to
70
55
  * reconstruct a ShapeDraft so the bounding-box overlay reappears for re-editing.
71
- *
72
- * Returns null if the click didn't land on a labeled voxel.
73
56
  */
74
57
  export function captureShapeDraftFromClick(nv: any): {
75
58
  ptA: any[];
@@ -210,11 +210,18 @@ function inferAxCorSagFromBounds(x1, y1, z1, x2, y2, z2, fallback) {
210
210
  return 2;
211
211
  return fallback;
212
212
  }
213
+ function sliceKey(axCorSag, x, y, z) {
214
+ if (axCorSag === 0)
215
+ return "".concat(x, ",").concat(y);
216
+ if (axCorSag === 1)
217
+ return "".concat(x, ",").concat(z);
218
+ return "".concat(y, ",").concat(z);
219
+ }
213
220
  /**
214
221
  * Flood-fill a connected voxel cluster from a seed.
215
222
  * @returns {{ label: number, visited: Set<number>, voxels: [number,number,number][], bounds: object } | null}
216
223
  */
217
- export function floodFillClusterFromVox(nv, seedVox) {
224
+ function floodFillClusterFromVox(nv, seedVox) {
218
225
  var _a;
219
226
  var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
220
227
  if (!dims || !nv.drawBitmap || !seedVox)
@@ -274,36 +281,15 @@ export function floodFillClusterFromVox(nv, seedVox) {
274
281
  bounds: { x1: x1, y1: y1, z1: z1, x2: x2, y2: y2, z2: z2 }
275
282
  };
276
283
  }
277
- /** True when a voxel belongs to the live draft overlay (drawn on top of baseBitmap). */
278
- export function isVoxelPartOfDraft(nv, draft, seedVox) {
279
- var _a;
280
- if (!(draft === null || draft === void 0 ? void 0 : draft.baseBitmap) || !(nv === null || nv === void 0 ? void 0 : nv.drawBitmap) || !seedVox)
281
- return false;
282
- var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
283
- if (!dims)
284
- return false;
285
- var dx = dims[1];
286
- var dy = dims[2];
287
- var idx = voxelIndex(seedVox[0], seedVox[1], seedVox[2], dx, dy);
288
- return (nv.drawBitmap[idx] === draft.penValue &&
289
- draft.baseBitmap[idx] !== draft.penValue);
290
- }
291
- export function eraseClusterFromBitmap(bitmap, visited) {
284
+ function eraseClusterFromBitmap(bitmap, visited) {
292
285
  var next = new Uint8Array(bitmap);
293
286
  visited.forEach(function (idx) {
294
287
  next[idx] = 0;
295
288
  });
296
289
  return next;
297
290
  }
298
- function sliceKey(axCorSag, x, y, z) {
299
- if (axCorSag === 0)
300
- return "".concat(x, ",").concat(y);
301
- if (axCorSag === 1)
302
- return "".concat(x, ",").concat(z);
303
- return "".concat(y, ",").concat(z);
304
- }
305
291
  /** Guess rectangle vs ellipse from the filled voxel pattern, not the active tool. */
306
- export function inferShapePenTypeFromCluster(cluster, axCorSag) {
292
+ function inferShapePenTypeFromCluster(cluster, axCorSag) {
307
293
  var bounds = cluster.bounds, voxels = cluster.voxels;
308
294
  var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
309
295
  var filled = new Set();
@@ -358,8 +344,6 @@ export function inferShapePenTypeFromCluster(cluster, axCorSag) {
358
344
  * When the user clicks on an existing filled ROI while the rectangle/ellipse tool
359
345
  * is active (but no draft is currently open), flood-fill the clicked cluster to
360
346
  * reconstruct a ShapeDraft so the bounding-box overlay reappears for re-editing.
361
- *
362
- * Returns null if the click didn't land on a labeled voxel.
363
347
  */
364
348
  export function captureShapeDraftFromClick(nv) {
365
349
  var seedVox = voxFromMouse(nv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.6.3",
3
+ "version": "4.6.6",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",