cloudmr-ux 4.8.4 → 4.8.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.
@@ -636,20 +636,9 @@ export default function CloudMrNiivueViewer(props) {
636
636
  if (next.kind === "polyline") {
637
637
  var count = (_c = (_b = next.vertices) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0;
638
638
  if (count >= 2) {
639
- // Redraw segments in the new color, then update the base so the
640
- // rubber-band preview also uses the new color.
641
639
  redrawPolylineDraft(nv, __assign(__assign({}, next), { filled: false }));
642
640
  if (nv.drawBitmap)
643
641
  nv._cloudMrPolylineBaseBitmap = nv.drawBitmap.slice();
644
- // Re-apply fill on top when polygon is closeable (base stays segments-only).
645
- if (count >= 3) {
646
- nv.drawPenAxCorSag = next.axCorSag;
647
- nv.drawPenFillPts = next.vertices.map(function (v) { return __spreadArray([], v, true); });
648
- nv._cloudMrSkipNextUndoBitmap = true;
649
- nv.drawPenFilled();
650
- nv.refreshDrawing(false, false);
651
- nv.drawScene();
652
- }
653
642
  }
654
643
  }
655
644
  else if (next.kind === "freehand") {
@@ -943,8 +932,8 @@ export default function CloudMrNiivueViewer(props) {
943
932
  }
944
933
  }
945
934
  function applyPenDraftHandler(_a) {
946
- var _b, _c, _d, _e, _f;
947
- var _g = _a === void 0 ? {} : _a, _h = _g.keepTool, keepTool = _h === void 0 ? false : _h;
935
+ var _b, _c, _d, _e, _f, _g;
936
+ var _h = _a === void 0 ? {} : _a, _j = _h.keepTool, keepTool = _j === void 0 ? false : _j;
948
937
  var draft = penDraftRef.current;
949
938
  if (!draft)
950
939
  return;
@@ -973,9 +962,37 @@ export default function CloudMrNiivueViewer(props) {
973
962
  setPolylineVertexCount(0);
974
963
  }
975
964
  else if (draft._registryId && nv._cloudMrPolylineRegistry) {
976
- // Was a committed polyline reopened as a freehand draft (move-only).
977
- // Remove the stale registry entry so it is not matched on future clicks.
978
- nv._cloudMrPolylineRegistry = nv._cloudMrPolylineRegistry.filter(function (e) { return e.id !== draft._registryId; });
965
+ // Was a committed polyline reopened as a freehand draft (move-only, possibly recolored).
966
+ // Re-register with the current penValue and voxel positions so future clicks
967
+ // open it with the correct (possibly changed) color.
968
+ var entryIdx = nv._cloudMrPolylineRegistry.findIndex(function (e) { return e.id === draft._registryId; });
969
+ if (entryIdx >= 0) {
970
+ var old = nv._cloudMrPolylineRegistry[entryIdx];
971
+ // Collect the newly committed voxel positions using the draft's current penValue.
972
+ var newIndices = new Set();
973
+ if (nv.drawBitmap && draft.baseBitmap) {
974
+ var pv = draft.penValue;
975
+ for (var i = 0; i < nv.drawBitmap.length; i++) {
976
+ if (nv.drawBitmap[i] === pv && draft.baseBitmap[i] !== pv)
977
+ newIndices.add(i);
978
+ }
979
+ }
980
+ // Fall back to stroke voxel positions if bitmap diff returned nothing.
981
+ if (newIndices.size === 0 && draft.strokeVoxels && ((_g = nv.back) === null || _g === void 0 ? void 0 : _g.dims)) {
982
+ var dx = nv.back.dims[1];
983
+ var dy = nv.back.dims[2];
984
+ for (var _i = 0, _k = draft.strokeVoxels; _i < _k.length; _i++) {
985
+ var _l = _k[_i], x = _l[0], y = _l[1], z = _l[2];
986
+ newIndices.add(x + y * dx + z * dx * dy);
987
+ }
988
+ }
989
+ if (newIndices.size > 0) {
990
+ nv._cloudMrPolylineRegistry[entryIdx] = __assign(__assign({}, old), { penValue: draft.penValue, voxelIndices: newIndices });
991
+ }
992
+ else {
993
+ nv._cloudMrPolylineRegistry = nv._cloudMrPolylineRegistry.filter(function (e) { return e.id !== draft._registryId; });
994
+ }
995
+ }
979
996
  }
980
997
  setPenDraft(null);
981
998
  penDraftRef.current = null;
@@ -1051,27 +1068,23 @@ export default function CloudMrNiivueViewer(props) {
1051
1068
  nv.opts.deferFreehandCommit = false;
1052
1069
  nv.opts.polylinePenMode = false;
1053
1070
  nvSetDrawingEnabled(false);
1071
+ // Sync palette to the reopened ROI's color so the user can change it.
1072
+ if (draft.penValue != null) {
1073
+ setDrawPen(draft.penValue);
1074
+ nv.setPenValue(draft.penValue, false);
1075
+ }
1054
1076
  };
1055
1077
  nv.onPolylineChange = function (count) {
1056
- var _a, _b;
1078
+ var _a;
1057
1079
  setPolylineVertexCount(count);
1058
1080
  if (drawShapeToolRef.current === "polyline" && count >= 2) {
1059
- var draft = polylineDraftFromNv(nv, { filled: count >= 3 });
1081
+ var draft = polylineDraftFromNv(nv, { filled: false });
1060
1082
  if (draft) {
1061
- if (count >= 3) {
1062
- // Show fill live on canvas after each vertex commit.
1063
- // Save/restore _cloudMrPolylineBaseBitmap (segments-only) so the
1064
- // rubber-band preview still works correctly on mouse move.
1065
- var savedBase = (_a = nv._cloudMrPolylineBaseBitmap) === null || _a === void 0 ? void 0 : _a.slice();
1066
- redrawPolylineDraft(nv, draft);
1067
- if (savedBase)
1068
- nv._cloudMrPolylineBaseBitmap = savedBase;
1069
- }
1070
1083
  setPenDraft(draft);
1071
1084
  penDraftRef.current = draft;
1072
1085
  }
1073
1086
  }
1074
- else if (((_b = penDraftRef.current) === null || _b === void 0 ? void 0 : _b.kind) === "polyline") {
1087
+ else if (((_a = penDraftRef.current) === null || _a === void 0 ? void 0 : _a.kind) === "polyline") {
1075
1088
  setPenDraft(null);
1076
1089
  penDraftRef.current = null;
1077
1090
  }
@@ -1294,6 +1307,11 @@ export default function CloudMrNiivueViewer(props) {
1294
1307
  nv.opts.penType = draft.penType;
1295
1308
  nv.opts.deferShapeCommit = true;
1296
1309
  nvSetDrawingEnabled(false);
1310
+ // Sync palette to the reopened shape's color so the user can change it.
1311
+ if (draft.penValue != null) {
1312
+ setDrawPen(draft.penValue);
1313
+ nv.setPenValue(draft.penValue, false);
1314
+ }
1297
1315
  };
1298
1316
  nv.onApplyActiveDraft = function () {
1299
1317
  applyActiveDraftIfAny();
@@ -12,6 +12,7 @@ import {
12
12
  import { NI_PEN_TYPE } from "./niivuePenType.js";
13
13
  import {
14
14
  addPolylineVertex,
15
+ POLYLINE_CLOSE,
15
16
  axCorSagFromMouse,
16
17
  cancelPolyline,
17
18
  finishPolyline,
@@ -1664,7 +1665,11 @@ Niivue.prototype.mouseUpListener = function cloudMrMouseUpListener() {
1664
1665
  _mouseUpListener.call(this);
1665
1666
 
1666
1667
  if (polylineClick) {
1667
- addPolylineVertex(this);
1668
+ const result = addPolylineVertex(this);
1669
+ if (result === POLYLINE_CLOSE && typeof this.onApplyActiveDraft === "function") {
1670
+ // Double-click detected — connect last vertex back to first, fill, and commit.
1671
+ this.onApplyActiveDraft();
1672
+ }
1668
1673
  return;
1669
1674
  }
1670
1675
 
@@ -92,7 +92,9 @@ export default function DrawColorPlatte(_a) {
92
92
  })
93
93
  }
94
94
  } }, { children: _jsx(FiberManualRecordIcon, { className: "color-swatch", sx: color.sx }) }), index));
95
- }) })), isPolylineTool && expanded && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: "Click to add vertices \u00B7 Right-click to fill" }))), penDraftActive &&
95
+ }) })), isPolylineTool && expanded && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: polylineVertexCount >= 3
96
+ ? "Double-click to close & fill · Right-click to cancel"
97
+ : "Click to add vertices" }))), penDraftActive &&
96
98
  expanded &&
97
99
  ((isFreehandTool && penDraftKind === "freehand") ||
98
100
  (isPolylineTool && penDraftKind === "polyline")) && (_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: "Delete this ROI drawing" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "delete pen draft", onClick: function () { return onDeletePenDraft === null || onDeletePenDraft === void 0 ? void 0 : onDeletePenDraft(); }, startIcon: _jsx(DeleteOutlineIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
@@ -6,7 +6,12 @@ export function notifyPolylineChange(nv: any, count: any): void;
6
6
  export function resetPolylineState(nv: any): void;
7
7
  export function cancelPolyline(nv: any): void;
8
8
  export function previewPolylineSegment(nv: any): void;
9
- export function addPolylineVertex(nv: any): boolean;
9
+ export function addPolylineVertex(nv: any): boolean | typeof POLYLINE_CLOSE;
10
10
  export function finishPolyline(nv: any, { fillClosed }?: {
11
11
  fillClosed?: boolean | undefined;
12
12
  }): boolean;
13
+ /**
14
+ * Sentinel returned by addPolylineVertex when a double-click is detected with
15
+ * ≥3 existing vertices. The caller should close the polygon and auto-commit.
16
+ */
17
+ export const POLYLINE_CLOSE: unique symbol;
@@ -46,6 +46,7 @@ export function resetPolylineState(nv) {
46
46
  nv._cloudMrPolylineBaseBitmap = null;
47
47
  nv._cloudMrPolylineSessionStartBitmap = null;
48
48
  nv._cloudMrPolylineAxCorSag = -1;
49
+ nv._cloudMrLastPolylineClickMs = null;
49
50
  notifyPolylineChange(nv, 0);
50
51
  }
51
52
  export function cancelPolyline(nv) {
@@ -69,12 +70,29 @@ export function previewPolylineSegment(nv) {
69
70
  nv.refreshDrawing(false, false);
70
71
  nv.drawScene();
71
72
  }
73
+ /** Max ms between two clicks to be considered a double-click. */
74
+ var DOUBLE_CLICK_MS = 400;
75
+ /**
76
+ * Sentinel returned by addPolylineVertex when a double-click is detected with
77
+ * ≥3 existing vertices. The caller should close the polygon and auto-commit.
78
+ */
79
+ export var POLYLINE_CLOSE = Symbol("POLYLINE_CLOSE");
72
80
  export function addPolylineVertex(nv) {
73
81
  var pt = voxFromMouse(nv);
74
82
  if (!pt)
75
83
  return false;
76
84
  var penValue = nv.opts.penValue;
77
85
  var verts = nv._cloudMrPolylineVertices || (nv._cloudMrPolylineVertices = []);
86
+ // Double-click detection: two clicks within DOUBLE_CLICK_MS on ≥3 vertices.
87
+ var now = Date.now();
88
+ var isDoubleClick = verts.length >= 3 &&
89
+ nv._cloudMrLastPolylineClickMs != null &&
90
+ now - nv._cloudMrLastPolylineClickMs <= DOUBLE_CLICK_MS;
91
+ nv._cloudMrLastPolylineClickMs = now;
92
+ if (isDoubleClick) {
93
+ // Don't add this click as a vertex — signal close instead.
94
+ return POLYLINE_CLOSE;
95
+ }
78
96
  if (verts.length === 0) {
79
97
  nv._cloudMrPolylineSessionStartBitmap = nv.drawBitmap.slice();
80
98
  nv._cloudMrPolylineBaseBitmap = nv._cloudMrPolylineSessionStartBitmap.slice();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.8.4",
3
+ "version": "4.8.6",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",