cloudmr-ux 4.5.3 → 4.5.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.
@@ -9,7 +9,7 @@ export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "rois" | "selectedR
9
9
  onCancelPolyline?: () => void;
10
10
  onApplyPenDraft?: () => void;
11
11
  onCancelPenDraft?: () => void;
12
- onCloseFillPenDraft?: () => void;
12
+ onFillPenDraft?: () => void;
13
13
  penDraftActive?: boolean;
14
14
  shapeDraftActive?: boolean;
15
15
  onApplyShapeDraft?: () => void;
@@ -57,6 +57,7 @@ export interface CloudMrNiivuePanelProps {
57
57
  penValue: number;
58
58
  vertices?: [number, number, number][];
59
59
  strokeVoxels?: [number, number, number][];
60
+ filled?: boolean;
60
61
  bounds?: {
61
62
  x1: number;
62
63
  y1: number;
@@ -63,7 +63,7 @@ import { SettingsPanel } from './SettingsPanel';
63
63
  import { NumberPicker } from './NumberPicker';
64
64
  import { ColorPicker } from './ColorPicker';
65
65
  import { LayersPanel } from './LayersPanel';
66
- import { applyPenDraft, cancelPenDraft, polylineDraftFromNv, syncPolylineDraftToNv, } from './penDraftUtils';
66
+ import { applyPenDraft, cancelPenDraft, fillPolylineDraft, polylineDraftFromNv, syncPolylineDraftToNv, } from './penDraftUtils';
67
67
  import { CloudMrNiivuePanel } from './CloudMrNiivuePanel';
68
68
  import { Niivue } from './NiivuePatcher';
69
69
  import NVSwitch from './Switch';
@@ -777,13 +777,12 @@ export default function CloudMrNiivueViewer(props) {
777
777
  nvSetDrawingEnabled(true);
778
778
  }
779
779
  }
780
- function applyPenDraftHandler(fillClosed) {
780
+ function applyPenDraftHandler() {
781
781
  var _a;
782
- if (fillClosed === void 0) { fillClosed = false; }
783
782
  var draft = penDraftRef.current;
784
783
  if (!draft)
785
784
  return;
786
- applyPenDraft(nv, draft, { fillClosed: fillClosed });
785
+ applyPenDraft(nv, draft);
787
786
  if (draft.kind === "polyline") {
788
787
  (_a = nv.cloudMrResetPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
789
788
  setPolylineVertexCount(0);
@@ -797,6 +796,13 @@ export default function CloudMrNiivueViewer(props) {
797
796
  }
798
797
  resampleImage();
799
798
  }
799
+ function fillPolylineDraftHandler() {
800
+ var draft = penDraftRef.current;
801
+ if (!draft || draft.kind !== "polyline" || draft.vertices.length < 3)
802
+ return;
803
+ var next = fillPolylineDraft(nv, draft);
804
+ onPenDraftChange(next);
805
+ }
800
806
  function onPenDraftChange(draft) {
801
807
  setPenDraft(draft);
802
808
  penDraftRef.current = draft;
@@ -814,16 +820,20 @@ export default function CloudMrNiivueViewer(props) {
814
820
  nvSetDrawingEnabled(false);
815
821
  };
816
822
  nv.onPolylineChange = function (count) {
817
- var _a;
823
+ var _a, _b;
818
824
  setPolylineVertexCount(count);
819
825
  if (penDrawModeRef.current === "polyline" && count >= 2) {
820
- var draft = polylineDraftFromNv(nv);
826
+ var prev = penDraftRef.current;
827
+ var preserveFill = (prev === null || prev === void 0 ? void 0 : prev.kind) === "polyline" &&
828
+ prev.filled &&
829
+ ((_a = prev.vertices) === null || _a === void 0 ? void 0 : _a.length) === count;
830
+ var draft = polylineDraftFromNv(nv, { filled: preserveFill });
821
831
  if (draft) {
822
832
  setPenDraft(draft);
823
833
  penDraftRef.current = draft;
824
834
  }
825
835
  }
826
- else if (((_a = penDraftRef.current) === null || _a === void 0 ? void 0 : _a.kind) === "polyline") {
836
+ else if (((_b = penDraftRef.current) === null || _b === void 0 ? void 0 : _b.kind) === "polyline") {
827
837
  setPenDraft(null);
828
838
  penDraftRef.current = null;
829
839
  }
@@ -865,6 +875,17 @@ export default function CloudMrNiivueViewer(props) {
865
875
  nv._cloudMrShapeDraftActive = true;
866
876
  nvSetDrawingEnabled(false);
867
877
  };
878
+ nv.onApplyActiveDraft = function () {
879
+ if (shapeDraftRef.current) {
880
+ applyShapeDraft();
881
+ }
882
+ else if (penDraftRef.current) {
883
+ applyPenDraftHandler();
884
+ }
885
+ };
886
+ React.useEffect(function () {
887
+ nv._cloudMrPenDraftActive = penDraft != null;
888
+ }, [penDraft]);
868
889
  React.useEffect(function () {
869
890
  if (!shapeDraft && !penDraft) {
870
891
  return undefined;
@@ -886,7 +907,7 @@ export default function CloudMrNiivueViewer(props) {
886
907
  applyShapeDraft();
887
908
  }
888
909
  else if (penDraft) {
889
- applyPenDraftHandler(false);
910
+ applyPenDraftHandler();
890
911
  }
891
912
  }
892
913
  };
@@ -1391,9 +1412,9 @@ export default function CloudMrNiivueViewer(props) {
1391
1412
  onPenDrawModeChange: syncPenDrawMode,
1392
1413
  polylineVertexCount: polylineVertexCount,
1393
1414
  onCancelPolyline: cancelPolylineDraft,
1394
- onApplyPenDraft: function () { return applyPenDraftHandler(false); },
1415
+ onApplyPenDraft: applyPenDraftHandler,
1395
1416
  onCancelPenDraft: cancelPenDraftHandler,
1396
- onCloseFillPenDraft: function () { return applyPenDraftHandler(true); },
1417
+ onFillPenDraft: fillPolylineDraftHandler,
1397
1418
  penDraftActive: penDraft != null,
1398
1419
  brushSize: brushSize,
1399
1420
  updateBrushSize: nvUpdateBrushSize
@@ -1412,7 +1433,7 @@ export default function CloudMrNiivueViewer(props) {
1412
1433
  props.rois[selectedROI].filename : undefined) }), props.niis[selectedVolume] != undefined && _jsx(CloudMrNiivuePanel, { nv: nv, transformFactors: transformFactors, decimalPrecision: decimalPrecision, locationData: locationData, locationTableVisible: locationTableVisible, pipelineID: props.pipelineID, resampleImage: resampleImage, rois: rois, drawToolkitProps: drawToolkitProps, drawShapeTool: drawShapeTool, setDrawShapeTool: setDrawShapeTool, mins: boundMins, maxs: boundMaxs, mms: mms, min: min, max: max, setMin: setMin, setMax: setMax, unzipAndRenderROI: unpackROI, zipAndSendROI: zipAndSendDrawingLayer, setLabelAlias: setLabelAlias, onAfterRoiUpload: function () {
1413
1434
  var _a;
1414
1435
  void ((_a = props.refreshPipelineRois) === null || _a === void 0 ? void 0 : _a.call(props));
1415
- }, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: applyShapeDraft, onCancelShapeDraft: cancelShapeDraft, penDraft: penDraft, onPenDraftChange: onPenDraftChange, onApplyPenDraft: function () { return applyPenDraftHandler(false); }, onCancelPenDraft: cancelPenDraftHandler }, "".concat(selectedVolume))] })));
1436
+ }, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: applyShapeDraft, onCancelShapeDraft: cancelShapeDraft, penDraft: penDraft, onPenDraftChange: onPenDraftChange, onApplyPenDraft: applyPenDraftHandler, onCancelPenDraft: cancelPenDraftHandler }, "".concat(selectedVolume))] })));
1416
1437
  }
1417
1438
  function niiToVolume(nii) {
1418
1439
  return {
@@ -1465,8 +1465,29 @@ Niivue.prototype.cloudMrResetPolyline = function cloudMrResetPolyline() {
1465
1465
  resetPolylineState(this);
1466
1466
  };
1467
1467
 
1468
+ const RIGHT_MOUSE_BUTTON = 2;
1469
+
1470
+ function cloudMrHasApplyableDraft(nv) {
1471
+ return !!(nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive);
1472
+ }
1473
+
1474
+ function cloudMrTryApplyDraftOnRightClick(nv, event) {
1475
+ if (!cloudMrHasApplyableDraft(nv)) {
1476
+ return false;
1477
+ }
1478
+ event.preventDefault();
1479
+ event.stopPropagation();
1480
+ if (typeof nv.onApplyActiveDraft === "function") {
1481
+ nv.onApplyActiveDraft();
1482
+ }
1483
+ return true;
1484
+ }
1485
+
1468
1486
  const _mouseDownListener = Niivue.prototype.mouseDownListener;
1469
1487
  Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
1488
+ if (e.button === RIGHT_MOUSE_BUTTON && cloudMrTryApplyDraftOnRightClick(this, e)) {
1489
+ return;
1490
+ }
1470
1491
  if (shouldDeferFreehandCommit(this) && this.drawBitmap) {
1471
1492
  this._cloudMrFreehandSessionStartBitmap = this.drawBitmap.slice();
1472
1493
  this._cloudMrFreehandAxCorSag = -1;
@@ -1480,6 +1501,15 @@ Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
1480
1501
  }
1481
1502
  };
1482
1503
 
1504
+ const _mouseContextMenuListener = Niivue.prototype.mouseContextMenuListener;
1505
+ Niivue.prototype.mouseContextMenuListener = function cloudMrMouseContextMenuListener(e) {
1506
+ if (cloudMrHasApplyableDraft(this)) {
1507
+ e.preventDefault();
1508
+ return;
1509
+ }
1510
+ return _mouseContextMenuListener.call(this, e);
1511
+ };
1512
+
1483
1513
  const _mouseClick = Niivue.prototype.mouseClick;
1484
1514
  Niivue.prototype.mouseClick = function cloudMrMouseClick(...args) {
1485
1515
  if (isPolylinePenActive(this)) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Adjust handles for polyline (vertex drag) or freehand (bbox move/resize) drafts.
2
+ * Adjust handles for polyline (vertex drag) or freehand (move only) drafts.
3
3
  */
4
4
  export function PenDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
5
5
  nv: any;
@@ -20,12 +20,15 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
20
20
  };
21
21
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
22
22
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
23
- import { boundsToFreehandCorners, redrawFreehandDraft, redrawPolylineDraft, resizeFreehandDraft, syncPolylineDraftToNv, translateFreehandDraft, translatePolylineVertices, updatePolylineVertex, } from "./penDraftUtils";
24
- import { canvasDeltaToVoxDelta, clientToCanvasPos, translatePt, voxToOverlayPos, voxUnderClient, } from "./shapeDraftUtils";
23
+ import { boundsToFreehandCorners, redrawFreehandDraft, redrawPolylineDraft, syncPolylineDraftToNv, translateFreehandDraft, translatePolylineVertices, updatePolylineVertex, } from "./penDraftUtils";
24
+ import { canvasDeltaToVoxDelta, clientToCanvasPos, voxToOverlayPos, voxUnderClient, } from "./shapeDraftUtils";
25
25
  var HANDLE_SIZE = 10;
26
26
  var ACCENT = "#580f8b";
27
+ function cloneFreehandDraft(draft) {
28
+ return __assign(__assign({}, draft), { strokeVoxels: draft.strokeVoxels.map(function (v) { return __spreadArray([], v, true); }), bounds: draft.bounds ? __assign({}, draft.bounds) : draft.bounds });
29
+ }
27
30
  /**
28
- * Adjust handles for polyline (vertex drag) or freehand (bbox move/resize) drafts.
31
+ * Adjust handles for polyline (vertex drag) or freehand (move only) drafts.
29
32
  */
30
33
  export function PenDraftOverlay(_a) {
31
34
  var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
@@ -55,12 +58,10 @@ export function PenDraftOverlay(_a) {
55
58
  return boundsToFreehandCorners(draft.bounds, draft.axCorSag);
56
59
  }, [draft, isPolyline, overlayKey]);
57
60
  var cornerCss = useMemo(function () {
58
- if (isPolyline)
59
- return vertexCss;
60
- return freehandCorners
61
- .map(function (vox) { return voxToOverlayPos(nv, vox, draft.axCorSag); })
62
- .filter(Boolean);
63
- }, [draft === null || draft === void 0 ? void 0 : draft.axCorSag, freehandCorners, isPolyline, nv, overlayKey, vertexCss]);
61
+ if (!isPolyline)
62
+ return [];
63
+ return vertexCss;
64
+ }, [isPolyline, vertexCss]);
64
65
  var centerCss = useMemo(function () {
65
66
  var _a;
66
67
  if (!draft)
@@ -82,10 +83,13 @@ export function PenDraftOverlay(_a) {
82
83
  return null;
83
84
  }, [draft, isPolyline, nv, overlayKey]);
84
85
  var boxStyle = useMemo(function () {
85
- if (cornerCss.length < 2)
86
+ var points = isPolyline ? cornerCss : freehandCorners
87
+ .map(function (vox) { return voxToOverlayPos(nv, vox, draft === null || draft === void 0 ? void 0 : draft.axCorSag); })
88
+ .filter(Boolean);
89
+ if (points.length < 2)
86
90
  return null;
87
- var xs = cornerCss.map(function (p) { return p.x; });
88
- var ys = cornerCss.map(function (p) { return p.y; });
91
+ var xs = points.map(function (p) { return p.x; });
92
+ var ys = points.map(function (p) { return p.y; });
89
93
  var left = Math.min.apply(Math, xs);
90
94
  var top = Math.min.apply(Math, ys);
91
95
  var width = Math.max.apply(Math, xs) - left;
@@ -93,7 +97,7 @@ export function PenDraftOverlay(_a) {
93
97
  if (width < 2 && height < 2)
94
98
  return null;
95
99
  return { left: left, top: top, width: Math.max(width, 2), height: Math.max(height, 2) };
96
- }, [cornerCss]);
100
+ }, [cornerCss, draft === null || draft === void 0 ? void 0 : draft.axCorSag, freehandCorners, isPolyline, nv, overlayKey]);
97
101
  var applyDraft = useCallback(function (nextDraft) {
98
102
  if (nextDraft.kind === "polyline") {
99
103
  redrawPolylineDraft(nv, nextDraft);
@@ -115,50 +119,50 @@ export function PenDraftOverlay(_a) {
115
119
  };
116
120
  onPointerMoveRef.current = function (event) {
117
121
  var drag = dragRef.current;
118
- var current = draftRef.current;
119
- if (!drag || !current)
122
+ if (!drag)
120
123
  return;
121
124
  event.preventDefault();
125
+ var canvas = nv.canvas || document.getElementById("niiCanvas");
126
+ if (!canvas)
127
+ return;
128
+ var startCanvas = clientToCanvasPos(canvas, drag.startClientX, drag.startClientY);
129
+ var endCanvas = clientToCanvasPos(canvas, event.clientX, event.clientY);
130
+ var delta = canvasDeltaToVoxDelta(nv, startCanvas, endCanvas);
122
131
  if (drag.mode === "move") {
123
- var canvas = nv.canvas || document.getElementById("niiCanvas");
124
- if (!canvas)
125
- return;
126
- var startCanvas = clientToCanvasPos(canvas, drag.startClientX, drag.startClientY);
127
- var endCanvas = clientToCanvasPos(canvas, event.clientX, event.clientY);
128
- var delta = canvasDeltaToVoxDelta(nv, startCanvas, endCanvas);
129
- if (current.kind === "polyline") {
130
- applyDraft(__assign(__assign({}, current), { vertices: translatePolylineVertices(drag.startVertices, delta) }));
132
+ if (drag.kind === "polyline") {
133
+ applyDraft(__assign(__assign({}, drag.startDraft), { vertices: translatePolylineVertices(drag.startVertices, delta) }));
131
134
  }
132
135
  else {
133
- applyDraft(translateFreehandDraft(current, delta));
136
+ applyDraft(translateFreehandDraft(drag.startFreehandDraft, delta));
134
137
  }
135
138
  return;
136
139
  }
137
140
  var vox = voxUnderClient(nv, event.clientX, event.clientY);
138
141
  if (!vox)
139
142
  return;
140
- if (current.kind === "polyline") {
141
- applyDraft(__assign(__assign({}, current), { vertices: updatePolylineVertex(drag.startVertices, drag.cornerIndex, vox) }));
142
- }
143
- else {
144
- applyDraft(resizeFreehandDraft(current, drag.cornerIndex, vox));
145
- }
143
+ applyDraft(__assign(__assign({}, drag.startDraft), { vertices: updatePolylineVertex(drag.startVertices, drag.cornerIndex, vox) }));
146
144
  };
147
145
  var startDrag = useCallback(function (event, mode, cornerIndex) {
148
146
  var _a, _b;
149
147
  if (cornerIndex === void 0) { cornerIndex = -1; }
150
148
  event.preventDefault();
151
149
  event.stopPropagation();
150
+ var current = draftRef.current;
151
+ if (!current)
152
+ return;
152
153
  dragRef.current = {
153
154
  mode: mode,
154
155
  cornerIndex: cornerIndex,
156
+ kind: current.kind,
155
157
  startClientX: event.clientX,
156
158
  startClientY: event.clientY,
157
- startVertices: (_b = (_a = draft.vertices) === null || _a === void 0 ? void 0 : _a.map(function (v) { return __spreadArray([], v, true); })) !== null && _b !== void 0 ? _b : []
159
+ startDraft: current,
160
+ startVertices: (_b = (_a = current.vertices) === null || _a === void 0 ? void 0 : _a.map(function (v) { return __spreadArray([], v, true); })) !== null && _b !== void 0 ? _b : [],
161
+ startFreehandDraft: current.kind === "freehand" ? cloneFreehandDraft(current) : null
158
162
  };
159
163
  window.addEventListener("pointermove", onPointerMoveRef.current);
160
164
  window.addEventListener("pointerup", finishDragRef.current);
161
- }, [draft.vertices]);
165
+ }, []);
162
166
  useEffect(function () { return function () {
163
167
  var _a;
164
168
  (_a = finishDragRef.current) === null || _a === void 0 ? void 0 : _a.call(finishDragRef);
@@ -196,6 +200,7 @@ export function PenDraftOverlay(_a) {
196
200
  background: "rgba(88, 15, 139, 0.08)",
197
201
  boxSizing: "border-box",
198
202
  pointerEvents: "none"
199
- } }), centerCss && (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "move"); }, style: __assign(__assign({}, handleStyle), { left: centerCss.x, top: centerCss.y, cursor: "move", borderRadius: 2, width: 12, height: 12, marginLeft: -6, marginTop: -6 }), title: "Move shape" })), cornerCss.map(function (pos, i) { return (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "corner", i); }, style: __assign(__assign({}, handleStyle), { left: pos.x, top: pos.y, cursor: isPolyline ? "grab" : "nwse-resize" }), title: isPolyline ? "Move vertex" : "Resize shape" }, "handle-".concat(i))); })] })));
203
+ } }), centerCss && (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "move"); }, style: __assign(__assign({}, handleStyle), { left: centerCss.x, top: centerCss.y, cursor: "move", borderRadius: 2, width: 12, height: 12, marginLeft: -6, marginTop: -6 }), title: "Move shape" })), isPolyline &&
204
+ cornerCss.map(function (pos, i) { return (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "corner", i); }, style: __assign(__assign({}, handleStyle), { left: pos.x, top: pos.y, cursor: "grab" }), title: "Move vertex" }, "handle-".concat(i))); })] })));
200
205
  }
201
206
  export default PenDraftOverlay;
@@ -1,4 +1,4 @@
1
- export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onApplyPenDraft, onCancelPenDraft, onCloseFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, 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;
@@ -9,7 +9,7 @@ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEna
9
9
  penDraftActive?: boolean | undefined;
10
10
  onApplyPenDraft: any;
11
11
  onCancelPenDraft: any;
12
- onCloseFillPenDraft: any;
12
+ onFillPenDraft: any;
13
13
  brushSize?: number | undefined;
14
14
  updateBrushSize: any;
15
15
  shapeDraftActive?: boolean | undefined;
@@ -37,7 +37,7 @@ var modeBtnSx = function (active) { return ({
37
37
  px: 0.75
38
38
  }); };
39
39
  export default function DrawColorPlatte(_a) {
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, onCloseFillPenDraft = _a.onCloseFillPenDraft, _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;
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;
41
41
  return (_jsxs(Stack, __assign({ style: {
42
42
  position: "absolute",
43
43
  top: "100%",
@@ -62,14 +62,14 @@ export default function DrawColorPlatte(_a) {
62
62
  py: 0.25,
63
63
  px: 0.75,
64
64
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
65
- } }, { children: "Cancel" })) })), _jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 1 }, { children: [penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: "Connect last point to first and fill" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "close and fill polyline", onClick: function () { return onCloseFillPenDraft === null || onCloseFillPenDraft === void 0 ? void 0 : onCloseFillPenDraft(); }, 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
66
  color: "#c9a0e8",
67
67
  fontSize: ACTION_FONT_SIZE,
68
68
  textTransform: "none",
69
69
  minWidth: 0,
70
70
  py: 0.25,
71
71
  px: 0.75
72
- } }, { children: "Close & fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply (Enter)" }, { 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: {
72
+ } }, { children: "Fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply (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
73
  color: "#c9a0e8",
74
74
  fontSize: ACTION_FONT_SIZE,
75
75
  textTransform: "none",
@@ -85,7 +85,7 @@ export default function DrawColorPlatte(_a) {
85
85
  py: 0.25,
86
86
  px: 0.75,
87
87
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
88
- } }, { children: "Cancel" })) })), _jsx(Tooltip, __assign({ title: "Apply shape (Enter)" }, { 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: {
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: {
89
89
  color: "#c9a0e8",
90
90
  fontSize: ACTION_FONT_SIZE,
91
91
  textTransform: "none",
@@ -172,7 +172,7 @@ export function MroDrawToolkit(props) {
172
172
  alignItems: "center",
173
173
  gap: 4,
174
174
  overflow: "visible"
175
- } }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, showPenModes: true, penDrawMode: props.penDrawMode, onPenDrawModeChange: props.onPenDrawModeChange, polylineVertexCount: props.polylineVertexCount, onCancelPolyline: props.onCancelPolyline, penDraftActive: props.penDraftActive, onApplyPenDraft: props.onApplyPenDraft, onCancelPenDraft: props.onCancelPenDraft, onCloseFillPenDraft: props.onCloseFillPenDraft, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "r" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "rectangle", onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "l" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Ellipse" }, { children: _jsx(IconButton, __assign({ "aria-label": "ellipse", size: "small", onClick: clickEllipse, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("ellipse")) }, { children: _jsx(CircleOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "l", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "ellipse", onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "e" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: toolBtnSx }, { children: filled || expandedOption !== "e" ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "Save screenshot" }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ "aria-label": "capture", size: "small", disabled: !vol, onClick: function () { return vol && props.nv.saveScene("".concat(vol.name, "_drawing.png")); }, sx: toolBtnSx }, { children: _jsx(CameraAltIcon, { sx: { color: ICON_COLOR } }) })) }) })), _jsx(Tooltip, __assign({ title: "Clear drawing" }, { children: _jsx(IconButton, __assign({ "aria-label": "delete", size: "small", onClick: function () {
175
+ } }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, showPenModes: true, penDrawMode: props.penDrawMode, onPenDrawModeChange: props.onPenDrawModeChange, polylineVertexCount: props.polylineVertexCount, onCancelPolyline: props.onCancelPolyline, penDraftActive: props.penDraftActive, onApplyPenDraft: props.onApplyPenDraft, onCancelPenDraft: props.onCancelPenDraft, onFillPenDraft: props.onFillPenDraft, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "r" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "rectangle", onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "l" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Ellipse" }, { children: _jsx(IconButton, __assign({ "aria-label": "ellipse", size: "small", onClick: clickEllipse, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("ellipse")) }, { children: _jsx(CircleOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "l", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "ellipse", onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "e" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: toolBtnSx }, { children: filled || expandedOption !== "e" ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "Save screenshot" }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ "aria-label": "capture", size: "small", disabled: !vol, onClick: function () { return vol && props.nv.saveScene("".concat(vol.name, "_drawing.png")); }, sx: toolBtnSx }, { children: _jsx(CameraAltIcon, { sx: { color: ICON_COLOR } }) })) }) })), _jsx(Tooltip, __assign({ title: "Clear drawing" }, { children: _jsx(IconButton, __assign({ "aria-label": "delete", size: "small", onClick: function () {
176
176
  props.nv.clearDrawing();
177
177
  props.resampleImage();
178
178
  props.setDrawingChanged(false);
@@ -8,19 +8,10 @@
8
8
  * @property {[number, number, number][]} [vertices]
9
9
  * @property {[number, number, number][]} [strokeVoxels]
10
10
  * @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
11
+ * @property {boolean} [filled]
11
12
  */
12
13
  export function isFreehandPenActive(nv: any): any;
13
14
  export function shouldDeferFreehandCommit(nv: any): any;
14
- /**
15
- * @typedef {Object} PenDraft
16
- * @property {PenDraftKind} kind
17
- * @property {Uint8Array} baseBitmap
18
- * @property {number} axCorSag
19
- * @property {number} penValue
20
- * @property {[number, number, number][]} [vertices]
21
- * @property {[number, number, number][]} [strokeVoxels]
22
- * @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
23
- */
24
15
  export function redrawPolylineDraft(nv: any, draft: any): void;
25
16
  export function translatePolylineVertices(vertices: any, delta: any): any;
26
17
  export function updatePolylineVertex(vertices: any, index: any, newVox: any): any;
@@ -44,16 +35,19 @@ export function redrawFreehandDraft(nv: any, draft: any): void;
44
35
  export function translateFreehandDraft(draft: any, delta: any): any;
45
36
  export function resizeFreehandDraft(draft: any, cornerIndex: any, newVox: any): any;
46
37
  export function boundsToFreehandCorners(bounds: any, axCorSag: any): any[][];
47
- export function polylineDraftFromNv(nv: any): {
38
+ export function polylineDraftFromNv(nv: any, { filled }?: {
39
+ filled?: boolean | undefined;
40
+ }): {
48
41
  kind: string;
49
42
  vertices: any;
50
43
  baseBitmap: Uint8Array;
51
44
  axCorSag: any;
52
45
  penValue: any;
46
+ filled: boolean;
53
47
  } | null;
54
- export function applyPenDraft(nv: any, draft: any, { fillClosed }?: {
55
- fillClosed?: boolean | undefined;
56
- }): void;
48
+ /** Fill polyline interior without closing the outline or committing the draft. */
49
+ export function fillPolylineDraft(nv: any, draft: any): any;
50
+ export function applyPenDraft(nv: any, draft: any): void;
57
51
  export function cancelPenDraft(nv: any, draft: any): void;
58
52
  export type PenDraftKind = 'polyline' | 'freehand';
59
53
  export type PenDraft = {
@@ -71,4 +65,5 @@ export type PenDraft = {
71
65
  z1: number;
72
66
  z2: number;
73
67
  } | undefined;
68
+ filled?: boolean | undefined;
74
69
  };
@@ -30,6 +30,7 @@ import { NI_PEN_TYPE } from "./niivuePenType";
30
30
  * @property {[number, number, number][]} [vertices]
31
31
  * @property {[number, number, number][]} [strokeVoxels]
32
32
  * @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
33
+ * @property {boolean} [filled]
33
34
  */
34
35
  export function isFreehandPenActive(nv) {
35
36
  return (nv.opts.isFilledPen &&
@@ -40,16 +41,6 @@ export function isFreehandPenActive(nv) {
40
41
  export function shouldDeferFreehandCommit(nv) {
41
42
  return !!nv.opts.deferFreehandCommit && isFreehandPenActive(nv);
42
43
  }
43
- /**
44
- * @typedef {Object} PenDraft
45
- * @property {PenDraftKind} kind
46
- * @property {Uint8Array} baseBitmap
47
- * @property {number} axCorSag
48
- * @property {number} penValue
49
- * @property {[number, number, number][]} [vertices]
50
- * @property {[number, number, number][]} [strokeVoxels]
51
- * @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
52
- */
53
44
  export function redrawPolylineDraft(nv, draft) {
54
45
  var _a;
55
46
  if (!((_a = draft === null || draft === void 0 ? void 0 : draft.vertices) === null || _a === void 0 ? void 0 : _a.length) || !draft.baseBitmap)
@@ -59,6 +50,12 @@ export function redrawPolylineDraft(nv, draft) {
59
50
  for (var i = 1; i < draft.vertices.length; i++) {
60
51
  nv.drawPenLine(draft.vertices[i], draft.vertices[i - 1], draft.penValue);
61
52
  }
53
+ if (draft.filled && draft.vertices.length >= 3) {
54
+ nv.drawPenAxCorSag = draft.axCorSag;
55
+ nv.drawPenFillPts = draft.vertices.map(function (v) { return __spreadArray([], v, true); });
56
+ nv._cloudMrSkipNextUndoBitmap = true;
57
+ nv.drawPenFilled();
58
+ }
62
59
  nv.refreshDrawing(false, false);
63
60
  nv.drawScene();
64
61
  }
@@ -220,7 +217,8 @@ export function boundsToFreehandCorners(bounds, axCorSag) {
220
217
  [x, y2, z2],
221
218
  ];
222
219
  }
223
- export function polylineDraftFromNv(nv) {
220
+ export function polylineDraftFromNv(nv, _a) {
221
+ var _b = _a === void 0 ? {} : _a, _c = _b.filled, filled = _c === void 0 ? false : _c;
224
222
  var vertices = nv._cloudMrPolylineVertices;
225
223
  var baseBitmap = nv._cloudMrPolylineSessionStartBitmap;
226
224
  if (!(vertices === null || vertices === void 0 ? void 0 : vertices.length) || vertices.length < 2 || !baseBitmap)
@@ -230,19 +228,28 @@ export function polylineDraftFromNv(nv) {
230
228
  vertices: vertices.map(function (v) { return __spreadArray([], v, true); }),
231
229
  baseBitmap: new Uint8Array(baseBitmap),
232
230
  axCorSag: nv._cloudMrPolylineAxCorSag,
233
- penValue: nv.opts.penValue
231
+ penValue: nv.opts.penValue,
232
+ filled: filled
234
233
  };
235
234
  }
236
- export function applyPenDraft(nv, draft, _a) {
237
- var _b = _a === void 0 ? {} : _a, _c = _b.fillClosed, fillClosed = _c === void 0 ? false : _c;
235
+ /** Fill polyline interior without closing the outline or committing the draft. */
236
+ export function fillPolylineDraft(nv, draft) {
237
+ if (!(draft === null || draft === void 0 ? void 0 : draft.vertices) || draft.vertices.length < 3)
238
+ return draft;
239
+ redrawPolylineDraft(nv, __assign(__assign({}, draft), { filled: false }));
240
+ nv.drawPenAxCorSag = draft.axCorSag;
241
+ nv.drawPenFillPts = draft.vertices.map(function (v) { return __spreadArray([], v, true); });
242
+ nv._cloudMrSkipNextUndoBitmap = true;
243
+ nv.drawPenFilled();
244
+ nv.refreshDrawing(false, false);
245
+ nv.drawScene();
246
+ var next = __assign(__assign({}, draft), { filled: true });
247
+ syncPolylineDraftToNv(nv, next);
248
+ return next;
249
+ }
250
+ export function applyPenDraft(nv, draft) {
238
251
  if (draft.kind === "polyline") {
239
252
  redrawPolylineDraft(nv, draft);
240
- if (fillClosed && draft.vertices.length >= 3) {
241
- nv.drawPenAxCorSag = draft.axCorSag;
242
- nv.drawPenLine(draft.vertices[0], draft.vertices[draft.vertices.length - 1], draft.penValue);
243
- nv.drawPenFillPts = draft.vertices.map(function (v) { return __spreadArray([], v, true); });
244
- nv.drawPenFilled();
245
- }
246
253
  }
247
254
  else {
248
255
  redrawFreehandDraft(nv, draft);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.5.3",
3
+ "version": "4.5.5",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",