cloudmr-ux 4.5.0 → 4.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,10 @@ export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "rois" | "selectedR
7
7
  onPenDrawModeChange?: (mode: "freehand" | "polyline") => void;
8
8
  polylineVertexCount?: number;
9
9
  onCancelPolyline?: () => void;
10
- onFinishPolyline?: () => void;
11
- onCloseFillPolyline?: () => void;
10
+ onApplyPenDraft?: () => void;
11
+ onCancelPenDraft?: () => void;
12
+ onCloseFillPenDraft?: () => void;
13
+ penDraftActive?: boolean;
12
14
  shapeDraftActive?: boolean;
13
15
  onApplyShapeDraft?: () => void;
14
16
  onCancelShapeDraft?: () => void;
@@ -48,5 +50,24 @@ export interface CloudMrNiivuePanelProps {
48
50
  onShapeDraftChange: (draft: import("./shapeDraftUtils").ShapeDraft) => void;
49
51
  onApplyShapeDraft: () => void;
50
52
  onCancelShapeDraft: () => void;
53
+ penDraft: {
54
+ kind: "polyline" | "freehand";
55
+ baseBitmap: Uint8Array;
56
+ axCorSag: number;
57
+ penValue: number;
58
+ vertices?: [number, number, number][];
59
+ strokeVoxels?: [number, number, number][];
60
+ bounds?: {
61
+ x1: number;
62
+ y1: number;
63
+ x2: number;
64
+ y2: number;
65
+ z1: number;
66
+ z2: number;
67
+ };
68
+ } | null;
69
+ onPenDraftChange: (draft: NonNullable<CloudMrNiivuePanelProps["penDraft"]>) => void;
70
+ onApplyPenDraft: () => void;
71
+ onCancelPenDraft: () => void;
51
72
  }
52
73
  export declare function CloudMrNiivuePanel(props: CloudMrNiivuePanelProps): import("react/jsx-runtime").JSX.Element;
@@ -56,6 +56,7 @@ import { NiivueContrastAdjustments } from "../niivue-contrast-adjustments/Niivue
56
56
  import { NiivueRoiTable } from "../niivue-roi-table/NiivueRoiTable";
57
57
  import { MroDrawToolkit } from "./mro-draw-toolkit/MroDrawToolkit";
58
58
  import { ShapeDraftOverlay } from "./ShapeDraftOverlay";
59
+ import { PenDraftOverlay } from "./PenDraftOverlay";
59
60
  export function CloudMrNiivuePanel(props) {
60
61
  var _this = this;
61
62
  var canvas = React.useRef(null);
@@ -110,12 +111,17 @@ export function CloudMrNiivuePanel(props) {
110
111
  if (props.shapeDraft) {
111
112
  props.onCancelShapeDraft();
112
113
  }
114
+ if (props.penDraft) {
115
+ props.onCancelPenDraft();
116
+ }
113
117
  if (tool !== "pen") {
114
118
  (_b = (_a = props.drawToolkitProps).onPenDrawModeChange) === null || _b === void 0 ? void 0 : _b.call(_a, "freehand");
115
119
  }
116
120
  props.setDrawShapeTool(tool);
117
121
  var nv = props.nv;
118
122
  nv.opts.deferShapeCommit = tool === "rectangle" || tool === "ellipse";
123
+ nv.opts.deferFreehandCommit =
124
+ tool === "pen" && props.drawToolkitProps.penDrawMode === "freehand";
119
125
  nv.opts.penType =
120
126
  tool === "rectangle"
121
127
  ? NI_PEN_TYPE.RECTANGLE
@@ -150,8 +156,11 @@ export function CloudMrNiivuePanel(props) {
150
156
  if (props.shapeDraft) {
151
157
  props.onCancelShapeDraft();
152
158
  }
159
+ if (props.penDraft) {
160
+ props.onCancelPenDraft();
161
+ }
153
162
  props.setDrawShapeTool(null);
154
- }, shapeDraftActive: props.shapeDraft != null, onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft, style: {
163
+ }, shapeDraftActive: props.shapeDraft != null, penDraftActive: props.penDraft != null, onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft, style: {
155
164
  marginBottom: 0,
156
165
  width: "100%",
157
166
  flexShrink: 0
@@ -182,7 +191,7 @@ export function CloudMrNiivuePanel(props) {
182
191
  left: 0,
183
192
  width: "100%",
184
193
  height: "100%"
185
- } }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange, overlayKey: props.mms }))] }))] })), _jsxs(Box, __assign({ sx: {
194
+ } }), 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: {
186
195
  width: {
187
196
  xs: "100%",
188
197
  md: "35%"
@@ -63,6 +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
67
  import { CloudMrNiivuePanel } from './CloudMrNiivuePanel';
67
68
  import { Niivue } from './NiivuePatcher';
68
69
  import NVSwitch from './Switch';
@@ -172,17 +173,22 @@ export default function CloudMrNiivueViewer(props) {
172
173
  var _23 = useState(null), shapeDraft = _23[0], setShapeDraft = _23[1];
173
174
  var _24 = useState("freehand"), penDrawMode = _24[0], setPenDrawMode = _24[1];
174
175
  var _25 = useState(0), polylineVertexCount = _25[0], setPolylineVertexCount = _25[1];
175
- var _26 = useState(1), brushSize = _26[0], setBrushSize = _26[1];
176
+ var _26 = useState(null), penDraft = _26[0], setPenDraft = _26[1];
177
+ var _27 = useState(1), brushSize = _27[0], setBrushSize = _27[1];
176
178
  var shapeDraftRef = React.useRef(null);
179
+ var penDraftRef = React.useRef(null);
177
180
  var drawShapeToolRef = React.useRef(null);
181
+ var penDrawModeRef = React.useRef("freehand");
178
182
  drawShapeToolRef.current = drawShapeTool;
179
183
  shapeDraftRef.current = shapeDraft;
184
+ penDraftRef.current = penDraft;
185
+ penDrawModeRef.current = penDrawMode;
180
186
  React.useEffect(function () {
181
187
  nv.opts.penBounds = 0;
182
188
  nv.opts.penSize = 1;
183
189
  }, []);
184
- var _27 = React.useState(1.0), gamma = _27[0], setGamma = _27[1];
185
- var _28 = React.useState(0), gammaKey = _28[0], setGammaKey = _28[1];
190
+ var _28 = React.useState(1.0), gamma = _28[0], setGamma = _28[1];
191
+ var _29 = React.useState(0), gammaKey = _29[0], setGammaKey = _29[1];
186
192
  // Niivue → React bridge so other places (Toolbar) can force the UI to reset
187
193
  nv.onResetGamma = function () {
188
194
  setGamma(1.0);
@@ -227,13 +233,13 @@ export default function CloudMrNiivueViewer(props) {
227
233
  // setLayers([...nv.volumes])
228
234
  // }, [])
229
235
  // values dualslider
230
- var _29 = useState(0), rangeKey = _29[0], setRangeKey = _29[1];
236
+ var _30 = useState(0), rangeKey = _30[0], setRangeKey = _30[1];
231
237
  nv.onResetContrast = function () {
232
238
  setRangeKey(rangeKey + 1);
233
239
  };
234
- var _30 = useState([0, 0, 0]), boundMins = _30[0], setBoundMins = _30[1];
235
- var _31 = useState([1, 1, 1]), boundMaxs = _31[0], setBoundMaxs = _31[1];
236
- var _32 = useState([0.5, 0.5, 0.5]), mms = _32[0], setMMs = _32[1];
240
+ var _31 = useState([0, 0, 0]), boundMins = _31[0], setBoundMins = _31[1];
241
+ var _32 = useState([1, 1, 1]), boundMaxs = _32[0], setBoundMaxs = _32[1];
242
+ var _33 = useState([0.5, 0.5, 0.5]), mms = _33[0], setMMs = _33[1];
237
243
  nv.onImageLoaded = function () {
238
244
  var _a, _b;
239
245
  var oldCrosshairPos = __spreadArray([], nv.scene.crosshairPos, true);
@@ -516,7 +522,7 @@ export default function CloudMrNiivueViewer(props) {
516
522
  setTextsVisible(true);
517
523
  }
518
524
  }
519
- var _33 = useState("pan"), dragMode = _33[0], setDragMode = _33[1];
525
+ var _34 = useState("pan"), dragMode = _34[0], setDragMode = _34[1];
520
526
  function nvSetDragMode(dragMode) {
521
527
  switch (dragMode) {
522
528
  case "none":
@@ -729,7 +735,7 @@ export default function CloudMrNiivueViewer(props) {
729
735
  nv.opts.crosshairWidth = w;
730
736
  nv.drawScene();
731
737
  }
732
- var _34 = useState({}), labelMapping = _34[0], setLabelMapping = _34[1];
738
+ var _35 = useState({}), labelMapping = _35[0], setLabelMapping = _35[1];
733
739
  function resampleImage(mapping) {
734
740
  if (mapping === void 0) { mapping = labelMapping; }
735
741
  var el = typeof document !== "undefined" ? document.getElementById("histoplot") : null;
@@ -745,30 +751,82 @@ export default function CloudMrNiivueViewer(props) {
745
751
  function syncPenDrawMode(mode) {
746
752
  var _a;
747
753
  setPenDrawMode(mode);
754
+ penDrawModeRef.current = mode;
748
755
  nv.opts.polylinePenMode = mode === "polyline";
749
756
  nv.opts.isFilledPen = mode === "freehand";
757
+ nv.opts.deferFreehandCommit =
758
+ drawShapeToolRef.current === "pen" && mode === "freehand";
750
759
  if (mode === "freehand") {
751
760
  (_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
752
761
  }
753
762
  }
754
- function cancelPolylineDraft() {
763
+ function cancelPenDraftHandler() {
755
764
  var _a;
756
- (_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
765
+ var draft = penDraftRef.current;
766
+ if (!draft)
767
+ return;
768
+ cancelPenDraft(nv, draft);
769
+ if (draft.kind === "polyline") {
770
+ (_a = nv.cloudMrResetPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
771
+ setPolylineVertexCount(0);
772
+ }
773
+ setPenDraft(null);
774
+ penDraftRef.current = null;
775
+ nv._cloudMrPenDraftActive = false;
776
+ if (drawShapeToolRef.current === "pen") {
777
+ nvSetDrawingEnabled(true);
778
+ }
757
779
  }
758
- function finishPolylineOpen() {
759
- if (nv.cloudMrFinishPolyline(false)) {
760
- setDrawingChanged(true);
761
- resampleImage();
780
+ function applyPenDraftHandler(fillClosed) {
781
+ var _a;
782
+ if (fillClosed === void 0) { fillClosed = false; }
783
+ var draft = penDraftRef.current;
784
+ if (!draft)
785
+ return;
786
+ applyPenDraft(nv, draft, { fillClosed: fillClosed });
787
+ if (draft.kind === "polyline") {
788
+ (_a = nv.cloudMrResetPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
789
+ setPolylineVertexCount(0);
762
790
  }
791
+ setPenDraft(null);
792
+ penDraftRef.current = null;
793
+ nv._cloudMrPenDraftActive = false;
794
+ setDrawingChanged(true);
795
+ if (drawShapeToolRef.current === "pen") {
796
+ nvSetDrawingEnabled(true);
797
+ }
798
+ resampleImage();
763
799
  }
764
- function finishPolylineClosed() {
765
- if (nv.cloudMrFinishPolyline(true)) {
766
- setDrawingChanged(true);
767
- resampleImage();
800
+ function onPenDraftChange(draft) {
801
+ setPenDraft(draft);
802
+ penDraftRef.current = draft;
803
+ if (draft.kind === "polyline") {
804
+ syncPolylineDraftToNv(nv, draft);
768
805
  }
769
806
  }
807
+ function cancelPolylineDraft() {
808
+ cancelPenDraftHandler();
809
+ }
810
+ nv.onPenDraftReady = function (draft) {
811
+ setPenDraft(draft);
812
+ penDraftRef.current = draft;
813
+ nv._cloudMrPenDraftActive = true;
814
+ nvSetDrawingEnabled(false);
815
+ };
770
816
  nv.onPolylineChange = function (count) {
817
+ var _a;
771
818
  setPolylineVertexCount(count);
819
+ if (penDrawModeRef.current === "polyline" && count >= 2) {
820
+ var draft = polylineDraftFromNv(nv);
821
+ if (draft) {
822
+ setPenDraft(draft);
823
+ penDraftRef.current = draft;
824
+ }
825
+ }
826
+ else if (((_a = penDraftRef.current) === null || _a === void 0 ? void 0 : _a.kind) === "polyline") {
827
+ setPenDraft(null);
828
+ penDraftRef.current = null;
829
+ }
772
830
  };
773
831
  function cancelShapeDraft() {
774
832
  var draft = shapeDraftRef.current;
@@ -808,7 +866,7 @@ export default function CloudMrNiivueViewer(props) {
808
866
  nvSetDrawingEnabled(false);
809
867
  };
810
868
  React.useEffect(function () {
811
- if (!shapeDraft && !(penDrawMode === "polyline" && polylineVertexCount >= 2)) {
869
+ if (!shapeDraft && !penDraft) {
812
870
  return undefined;
813
871
  }
814
872
  var onKeyDown = function (event) {
@@ -817,8 +875,8 @@ export default function CloudMrNiivueViewer(props) {
817
875
  if (shapeDraft) {
818
876
  cancelShapeDraft();
819
877
  }
820
- else if (polylineVertexCount >= 2) {
821
- cancelPolylineDraft();
878
+ else if (penDraft) {
879
+ cancelPenDraftHandler();
822
880
  }
823
881
  return;
824
882
  }
@@ -827,19 +885,19 @@ export default function CloudMrNiivueViewer(props) {
827
885
  if (shapeDraft) {
828
886
  applyShapeDraft();
829
887
  }
830
- else if (penDrawMode === "polyline" && polylineVertexCount >= 2) {
831
- finishPolylineOpen();
888
+ else if (penDraft) {
889
+ applyPenDraftHandler(false);
832
890
  }
833
891
  }
834
892
  };
835
893
  window.addEventListener("keydown", onKeyDown);
836
894
  return function () { return window.removeEventListener("keydown", onKeyDown); };
837
- }, [shapeDraft, penDrawMode, polylineVertexCount]);
895
+ }, [shapeDraft, penDraft]);
838
896
  function nvUpdateSelectionBoxColor(rgb01) {
839
897
  setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
840
898
  nv.setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
841
899
  }
842
- var _35 = React.useState('axial'), sliceType = _35[0], setSliceType = _35[1];
900
+ var _36 = React.useState('axial'), sliceType = _36[0], setSliceType = _36[1];
843
901
  function nvUpdateSliceType(newSliceType) {
844
902
  setSliceType(newSliceType);
845
903
  if (newSliceType === 'axial') {
@@ -979,14 +1037,14 @@ export default function CloudMrNiivueViewer(props) {
979
1037
  return [2 /*return*/];
980
1038
  });
981
1039
  }); };
982
- var _36 = useState(''), selectedROI = _36[0], setSelectedDrawingLayer = _36[1];
983
- var _37 = useState(false), saveDialogOpen = _37[0], setSaveDialogOpen = _37[1];
984
- var _38 = useState(function () {
985
- }), saveConfirmCallback = _38[0], setSaveConfirmCallback = _38[1];
986
- var _39 = useState(false), confirmationOpen = _39[0], setConfirmationOpen = _39[1];
987
- var _40 = useState(function () { }), warningConfirmationCallback = _40[0], setWarningConfirmationCallback = _40[1];
988
- var _41 = useState(function () { }), warningCancelCallback = _41[0], setWarningCancelCallback = _41[1];
989
- var _42 = useState(false), drawingChanged = _42[0], setDrawingChanged = _42[1];
1040
+ var _37 = useState(''), selectedROI = _37[0], setSelectedDrawingLayer = _37[1];
1041
+ var _38 = useState(false), saveDialogOpen = _38[0], setSaveDialogOpen = _38[1];
1042
+ var _39 = useState(function () {
1043
+ }), saveConfirmCallback = _39[0], setSaveConfirmCallback = _39[1];
1044
+ var _40 = useState(false), confirmationOpen = _40[0], setConfirmationOpen = _40[1];
1045
+ var _41 = useState(function () { }), warningConfirmationCallback = _41[0], setWarningConfirmationCallback = _41[1];
1046
+ var _42 = useState(function () { }), warningCancelCallback = _42[0], setWarningCancelCallback = _42[1];
1047
+ var _43 = useState(false), drawingChanged = _43[0], setDrawingChanged = _43[1];
990
1048
  // When new drawing strokes are made on top of a saved ROI, reset the dropdown
991
1049
  // so it shows the "ROI Layer" placeholder instead of the stale saved name.
992
1050
  React.useEffect(function () {
@@ -1313,8 +1371,8 @@ export default function CloudMrNiivueViewer(props) {
1313
1371
  cancelShapeDraft();
1314
1372
  return;
1315
1373
  }
1316
- if (polylineVertexCount > 0) {
1317
- cancelPolylineDraft();
1374
+ if (penDraftRef.current) {
1375
+ cancelPenDraftHandler();
1318
1376
  return;
1319
1377
  }
1320
1378
  nv.drawUndo();
@@ -1333,8 +1391,10 @@ export default function CloudMrNiivueViewer(props) {
1333
1391
  onPenDrawModeChange: syncPenDrawMode,
1334
1392
  polylineVertexCount: polylineVertexCount,
1335
1393
  onCancelPolyline: cancelPolylineDraft,
1336
- onFinishPolyline: finishPolylineOpen,
1337
- onCloseFillPolyline: finishPolylineClosed,
1394
+ onApplyPenDraft: function () { return applyPenDraftHandler(false); },
1395
+ onCancelPenDraft: cancelPenDraftHandler,
1396
+ onCloseFillPenDraft: function () { return applyPenDraftHandler(true); },
1397
+ penDraftActive: penDraft != null,
1338
1398
  brushSize: brushSize,
1339
1399
  updateBrushSize: nvUpdateBrushSize
1340
1400
  };
@@ -1352,7 +1412,7 @@ export default function CloudMrNiivueViewer(props) {
1352
1412
  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 () {
1353
1413
  var _a;
1354
1414
  void ((_a = props.refreshPipelineRois) === null || _a === void 0 ? void 0 : _a.call(props));
1355
- }, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: applyShapeDraft, onCancelShapeDraft: cancelShapeDraft }, "".concat(selectedVolume))] })));
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))] })));
1356
1416
  }
1357
1417
  function niiToVolume(nii) {
1358
1418
  return {
@@ -9,6 +9,7 @@ import {
9
9
  } from "./shapeDraftUtils.js";
10
10
  import {
11
11
  addPolylineVertex,
12
+ axCorSagFromMouse,
12
13
  cancelPolyline,
13
14
  finishPolyline,
14
15
  isClickWithoutDrag,
@@ -16,6 +17,10 @@ import {
16
17
  previewPolylineSegment,
17
18
  resetPolylineState,
18
19
  } from "./polylinePenUtils.js";
20
+ import {
21
+ captureFreehandDraft,
22
+ shouldDeferFreehandCommit,
23
+ } from "./penDraftUtils.js";
19
24
 
20
25
  /*
21
26
  * bitmapOverlay — in-browser state only: remembers each voxel's label *before* Group
@@ -496,6 +501,31 @@ Niivue.prototype.drawPenFilled = function () {
496
501
  this.drawBitmap[index] = value;
497
502
  }
498
503
  })
504
+ if (shouldDeferFreehandCommit(this) && this._cloudMrFreehandSessionStartBitmap) {
505
+ this._cloudMrSkipNextUndoBitmap = true;
506
+ this.hiddenBitmap = new Uint8Array(this.drawBitmap.length);
507
+ for (let i = 0; i < this.drawBitmap.length; i++) {
508
+ let pen = this.drawBitmap[i];
509
+ if (!this.getLabelVisibility(pen)) {
510
+ this.hiddenBitmap[i] = this.drawBitmap[i];
511
+ this.drawBitmap[i] = 0;
512
+ }
513
+ }
514
+ this.refreshDrawing(false);
515
+ this.drawScene();
516
+ const axCorSag =
517
+ this.drawPenAxCorSag >= 0 ? this.drawPenAxCorSag : this._cloudMrFreehandAxCorSag;
518
+ const draft = captureFreehandDraft(
519
+ this,
520
+ this._cloudMrFreehandSessionStartBitmap,
521
+ axCorSag,
522
+ );
523
+ this._cloudMrFreehandSessionStartBitmap = null;
524
+ if (draft && typeof this.onPenDraftReady === "function") {
525
+ this.onPenDraftReady(draft);
526
+ }
527
+ return;
528
+ }
499
529
  this.drawAddUndoBitmap()
500
530
  // Post-processing to hide hidden voxels
501
531
  this.hiddenBitmap = new Uint8Array(this.drawBitmap.length);
@@ -1435,6 +1465,21 @@ Niivue.prototype.cloudMrResetPolyline = function cloudMrResetPolyline() {
1435
1465
  resetPolylineState(this);
1436
1466
  };
1437
1467
 
1468
+ const _mouseDownListener = Niivue.prototype.mouseDownListener;
1469
+ Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
1470
+ if (shouldDeferFreehandCommit(this) && this.drawBitmap) {
1471
+ this._cloudMrFreehandSessionStartBitmap = this.drawBitmap.slice();
1472
+ this._cloudMrFreehandAxCorSag = -1;
1473
+ }
1474
+ _mouseDownListener.call(this, e);
1475
+ if (shouldDeferFreehandCommit(this) && this._cloudMrFreehandSessionStartBitmap) {
1476
+ const axCorSag = axCorSagFromMouse(this);
1477
+ if (axCorSag >= 0) {
1478
+ this._cloudMrFreehandAxCorSag = axCorSag;
1479
+ }
1480
+ }
1481
+ };
1482
+
1438
1483
  const _mouseClick = Niivue.prototype.mouseClick;
1439
1484
  Niivue.prototype.mouseClick = function cloudMrMouseClick(...args) {
1440
1485
  if (isPolylinePenActive(this)) {
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Adjust handles for polyline (vertex drag) or freehand (bbox move/resize) drafts.
3
+ */
4
+ export function PenDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
5
+ nv: any;
6
+ draft: any;
7
+ onDraftChange: any;
8
+ overlayKey: any;
9
+ }): import("react/jsx-runtime").JSX.Element | null;
10
+ export default PenDraftOverlay;
@@ -0,0 +1,201 @@
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
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
13
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
14
+ if (ar || !(i in from)) {
15
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
16
+ ar[i] = from[i];
17
+ }
18
+ }
19
+ return to.concat(ar || Array.prototype.slice.call(from));
20
+ };
21
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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";
25
+ var HANDLE_SIZE = 10;
26
+ var ACCENT = "#580f8b";
27
+ /**
28
+ * Adjust handles for polyline (vertex drag) or freehand (bbox move/resize) drafts.
29
+ */
30
+ export function PenDraftOverlay(_a) {
31
+ var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
32
+ var dragRef = useRef(null);
33
+ var draftRef = useRef(draft);
34
+ draftRef.current = draft;
35
+ var onPointerMoveRef = useRef(null);
36
+ var finishDragRef = useRef(null);
37
+ var _b = useState(0), setTick = _b[1];
38
+ var bump = useCallback(function () { return setTick(function (t) { return t + 1; }); }, []);
39
+ useEffect(function () {
40
+ var onMove = function () { return bump(); };
41
+ window.addEventListener("resize", onMove);
42
+ return function () { return window.removeEventListener("resize", onMove); };
43
+ }, [bump]);
44
+ var isPolyline = (draft === null || draft === void 0 ? void 0 : draft.kind) === "polyline";
45
+ var vertexCss = useMemo(function () {
46
+ if (!isPolyline || !(draft === null || draft === void 0 ? void 0 : draft.vertices))
47
+ return [];
48
+ return draft.vertices
49
+ .map(function (vox) { return voxToOverlayPos(nv, vox, draft.axCorSag); })
50
+ .filter(Boolean);
51
+ }, [draft, isPolyline, nv, overlayKey]);
52
+ var freehandCorners = useMemo(function () {
53
+ if (isPolyline || !(draft === null || draft === void 0 ? void 0 : draft.bounds))
54
+ return [];
55
+ return boundsToFreehandCorners(draft.bounds, draft.axCorSag);
56
+ }, [draft, isPolyline, overlayKey]);
57
+ 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]);
64
+ var centerCss = useMemo(function () {
65
+ var _a;
66
+ if (!draft)
67
+ return null;
68
+ if (isPolyline && ((_a = draft.vertices) === null || _a === void 0 ? void 0 : _a.length)) {
69
+ var xs = draft.vertices.map(function (v) { return v[0]; });
70
+ var ys = draft.vertices.map(function (v) { return v[1]; });
71
+ var zs = draft.vertices.map(function (v) { return v[2]; });
72
+ return voxToOverlayPos(nv, [
73
+ (Math.min.apply(Math, xs) + Math.max.apply(Math, xs)) / 2,
74
+ (Math.min.apply(Math, ys) + Math.max.apply(Math, ys)) / 2,
75
+ (Math.min.apply(Math, zs) + Math.max.apply(Math, zs)) / 2,
76
+ ], draft.axCorSag);
77
+ }
78
+ if (draft.bounds) {
79
+ var b = draft.bounds;
80
+ return voxToOverlayPos(nv, [(b.x1 + b.x2) / 2, (b.y1 + b.y2) / 2, (b.z1 + b.z2) / 2], draft.axCorSag);
81
+ }
82
+ return null;
83
+ }, [draft, isPolyline, nv, overlayKey]);
84
+ var boxStyle = useMemo(function () {
85
+ if (cornerCss.length < 2)
86
+ return null;
87
+ var xs = cornerCss.map(function (p) { return p.x; });
88
+ var ys = cornerCss.map(function (p) { return p.y; });
89
+ var left = Math.min.apply(Math, xs);
90
+ var top = Math.min.apply(Math, ys);
91
+ var width = Math.max.apply(Math, xs) - left;
92
+ var height = Math.max.apply(Math, ys) - top;
93
+ if (width < 2 && height < 2)
94
+ return null;
95
+ return { left: left, top: top, width: Math.max(width, 2), height: Math.max(height, 2) };
96
+ }, [cornerCss]);
97
+ var applyDraft = useCallback(function (nextDraft) {
98
+ if (nextDraft.kind === "polyline") {
99
+ redrawPolylineDraft(nv, nextDraft);
100
+ syncPolylineDraftToNv(nv, nextDraft);
101
+ }
102
+ else {
103
+ redrawFreehandDraft(nv, nextDraft);
104
+ }
105
+ onDraftChange(nextDraft);
106
+ }, [nv, onDraftChange]);
107
+ finishDragRef.current = function () {
108
+ dragRef.current = null;
109
+ if (onPointerMoveRef.current) {
110
+ window.removeEventListener("pointermove", onPointerMoveRef.current);
111
+ }
112
+ if (finishDragRef.current) {
113
+ window.removeEventListener("pointerup", finishDragRef.current);
114
+ }
115
+ };
116
+ onPointerMoveRef.current = function (event) {
117
+ var drag = dragRef.current;
118
+ var current = draftRef.current;
119
+ if (!drag || !current)
120
+ return;
121
+ event.preventDefault();
122
+ 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) }));
131
+ }
132
+ else {
133
+ applyDraft(translateFreehandDraft(current, delta));
134
+ }
135
+ return;
136
+ }
137
+ var vox = voxUnderClient(nv, event.clientX, event.clientY);
138
+ if (!vox)
139
+ 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
+ }
146
+ };
147
+ var startDrag = useCallback(function (event, mode, cornerIndex) {
148
+ var _a, _b;
149
+ if (cornerIndex === void 0) { cornerIndex = -1; }
150
+ event.preventDefault();
151
+ event.stopPropagation();
152
+ dragRef.current = {
153
+ mode: mode,
154
+ cornerIndex: cornerIndex,
155
+ startClientX: event.clientX,
156
+ 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 : []
158
+ };
159
+ window.addEventListener("pointermove", onPointerMoveRef.current);
160
+ window.addEventListener("pointerup", finishDragRef.current);
161
+ }, [draft.vertices]);
162
+ useEffect(function () { return function () {
163
+ var _a;
164
+ (_a = finishDragRef.current) === null || _a === void 0 ? void 0 : _a.call(finishDragRef);
165
+ }; }, []);
166
+ if (!draft || !boxStyle)
167
+ return null;
168
+ var handleStyle = {
169
+ position: "absolute",
170
+ width: HANDLE_SIZE,
171
+ height: HANDLE_SIZE,
172
+ marginLeft: -HANDLE_SIZE / 2,
173
+ marginTop: -HANDLE_SIZE / 2,
174
+ borderRadius: "50%",
175
+ background: "#fff",
176
+ border: "2px solid ".concat(ACCENT),
177
+ boxSizing: "border-box",
178
+ cursor: "pointer",
179
+ zIndex: 3,
180
+ touchAction: "none",
181
+ pointerEvents: "auto"
182
+ };
183
+ return (_jsxs("div", __assign({ style: {
184
+ position: "absolute",
185
+ inset: 0,
186
+ zIndex: 2,
187
+ pointerEvents: "none",
188
+ overflow: "hidden"
189
+ }, "aria-label": "Adjust pen ROI" }, { children: [_jsx("div", { style: {
190
+ position: "absolute",
191
+ left: boxStyle.left,
192
+ top: boxStyle.top,
193
+ width: boxStyle.width,
194
+ height: boxStyle.height,
195
+ border: "2px dashed ".concat(ACCENT),
196
+ background: "rgba(88, 15, 139, 0.08)",
197
+ boxSizing: "border-box",
198
+ 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))); })] })));
200
+ }
201
+ export default PenDraftOverlay;
@@ -1,4 +1,4 @@
1
- export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, onCancelPolyline, onFinishPolyline, onCloseFillPolyline, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
1
+ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onApplyPenDraft, onCancelPenDraft, onCloseFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
2
2
  expanded: any;
3
3
  updateDrawPen: any;
4
4
  setDrawingEnabled: any;
@@ -6,9 +6,10 @@ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEna
6
6
  penDrawMode?: string | undefined;
7
7
  onPenDrawModeChange: any;
8
8
  polylineVertexCount?: number | undefined;
9
- onCancelPolyline: any;
10
- onFinishPolyline: any;
11
- onCloseFillPolyline: any;
9
+ penDraftActive?: boolean | undefined;
10
+ onApplyPenDraft: any;
11
+ onCancelPenDraft: any;
12
+ onCloseFillPenDraft: any;
12
13
  brushSize?: number | undefined;
13
14
  updateBrushSize: any;
14
15
  shapeDraftActive?: boolean | undefined;
@@ -11,8 +11,7 @@ var __assign = (this && this.__assign) || function () {
11
11
  };
12
12
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  /**
14
- * Shared ROI label color row (NiiVue pen indices 1–6). Used by pen, rectangle, and ellipse tools.
15
- * Pen palette adds freehand vs straight-line mode; rectangle/ellipse show Apply/Cancel while adjusting.
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";
@@ -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, onCancelPolyline = _a.onCancelPolyline, onFinishPolyline = _a.onFinishPolyline, onCloseFillPolyline = _a.onCloseFillPolyline, _e = _a.brushSize, brushSize = _e === void 0 ? 1 : _e, updateBrushSize = _a.updateBrushSize, _f = _a.shapeDraftActive, shapeDraftActive = _f === void 0 ? false : _f, 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, 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;
42
41
  return (_jsxs(Stack, __assign({ style: {
43
42
  position: "absolute",
44
43
  top: "100%",
@@ -52,10 +51,10 @@ export default function DrawColorPlatte(_a) {
52
51
  borderTopLeftRadius: "6pt",
53
52
  borderTopRightRadius: "6pt",
54
53
  background: "#333"
55
- }, direction: "column", spacing: 0.5, sx: { py: expanded ? 0.5 : 0 } }, { children: [showPenModes && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 0.5, sx: { px: 0.75, pt: 0.25 } }, { children: [_jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("freehand"); }, sx: modeBtnSx(penDrawMode === "freehand") }, { children: "Freehand" })), _jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("polyline"); }, sx: modeBtnSx(penDrawMode === "polyline") }, { children: "Straight lines" }))] }))), showPenModes && expanded && updateBrushSize && (_jsx(BrushSizeSlider, { label: "Line thickness", brushSize: brushSize, updateBrushSize: updateBrushSize })), _jsx(Stack, __assign({ direction: "row" }, { children: FILLED_COLORS.map(function (color, index) { return (_jsx(IconButton, __assign({ onClick: function () {
54
+ }, direction: "column", spacing: 0.5, sx: { py: expanded ? 0.5 : 0 } }, { children: [showPenModes && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 0.5, sx: { px: 0.75, pt: 0.25 } }, { children: [_jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("freehand"); }, sx: modeBtnSx(penDrawMode === "freehand") }, { children: "Freehand" })), _jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("polyline"); }, sx: modeBtnSx(penDrawMode === "polyline") }, { children: "Polyline" }))] }))), showPenModes && expanded && updateBrushSize && (_jsx(BrushSizeSlider, { label: "Line thickness", brushSize: brushSize, updateBrushSize: updateBrushSize })), _jsx(Stack, __assign({ direction: "row" }, { children: FILLED_COLORS.map(function (color, index) { return (_jsx(IconButton, __assign({ onClick: function () {
56
55
  updateDrawPen({ target: { value: index + 1 } });
57
56
  setDrawingEnabled(true);
58
- } }, { children: _jsx(FiberManualRecordIcon, { sx: color.sx }) }), index)); }) })), showPenModes && penDrawMode === "polyline" && expanded && polylineVertexCount === 0 && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: "Click each corner to draw connected straight lines" }))), showPenModes && penDrawMode === "polyline" && expanded && polylineVertexCount >= 2 && (_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: "Discard current outline" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "cancel polyline", onClick: function () { return onCancelPolyline === null || onCancelPolyline === void 0 ? void 0 : onCancelPolyline(); }, startIcon: _jsx(CloseIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
57
+ } }, { children: _jsx(FiberManualRecordIcon, { sx: color.sx }) }), index)); }) })), showPenModes && penDrawMode === "polyline" && expanded && polylineVertexCount === 0 && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: "Click each vertex to draw connected line segments" }))), showPenModes && penDraftActive && 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 (Esc)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "cancel pen draft", onClick: function () { return onCancelPenDraft === null || onCancelPenDraft === void 0 ? void 0 : onCancelPenDraft(); }, startIcon: _jsx(CloseIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
59
58
  color: "#ccc",
60
59
  fontSize: ACTION_FONT_SIZE,
61
60
  textTransform: "none",
@@ -63,14 +62,14 @@ 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" })) })), _jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 1 }, { children: [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 onCloseFillPolyline === null || onCloseFillPolyline === void 0 ? void 0 : onCloseFillPolyline(); }, sx: {
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: {
67
66
  color: "#c9a0e8",
68
67
  fontSize: ACTION_FONT_SIZE,
69
68
  textTransform: "none",
70
69
  minWidth: 0,
71
70
  py: 0.25,
72
71
  px: 0.75
73
- } }, { children: "Close & fill" })) }))), _jsx(Tooltip, __assign({ title: "Keep outline only (Enter)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "finish polyline", onClick: function () { return onFinishPolyline === null || onFinishPolyline === void 0 ? void 0 : onFinishPolyline(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
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: {
74
73
  color: "#c9a0e8",
75
74
  fontSize: ACTION_FONT_SIZE,
76
75
  textTransform: "none",
@@ -78,7 +77,7 @@ export default function DrawColorPlatte(_a) {
78
77
  py: 0.25,
79
78
  px: 0.75,
80
79
  "& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
81
- } }, { children: "Done" })) }))] }))] }))), 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: {
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: {
82
81
  color: "#ccc",
83
82
  fontSize: ACTION_FONT_SIZE,
84
83
  textTransform: "none",
@@ -71,13 +71,15 @@ export function MroDrawToolkit(props) {
71
71
  var _d = useState(undefined), setMaskColor = _d[1];
72
72
  var filled = props.drawPen > 7;
73
73
  useEffect(function () {
74
- if (!props.shapeDraftActive)
74
+ if (!props.shapeDraftActive && !props.penDraftActive)
75
75
  return;
76
76
  if (drawShapeTool === "rectangle")
77
77
  setExpandedOption("r");
78
78
  else if (drawShapeTool === "ellipse")
79
79
  setExpandedOption("l");
80
- }, [props.shapeDraftActive, drawShapeTool]);
80
+ else if (drawShapeTool === "pen")
81
+ setExpandedOption("d");
82
+ }, [props.shapeDraftActive, props.penDraftActive, drawShapeTool]);
81
83
  var shapeSelectedSx = function (shape) {
82
84
  return drawShapeTool === shape
83
85
  ? { backgroundColor: "rgba(88, 15, 139, 0.12)", color: "#580f8b" }
@@ -149,7 +151,7 @@ export function MroDrawToolkit(props) {
149
151
  var _a;
150
152
  if (clickTargetIsNiivueCanvas(event.target))
151
153
  return;
152
- if (props.shapeDraftActive)
154
+ if (props.shapeDraftActive || props.penDraftActive)
153
155
  return;
154
156
  setExpandedOption("n");
155
157
  setExpandOpacityOptions(false);
@@ -170,7 +172,7 @@ export function MroDrawToolkit(props) {
170
172
  alignItems: "center",
171
173
  gap: 4,
172
174
  overflow: "visible"
173
- } }, { 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, onFinishPolyline: props.onFinishPolyline, onCloseFillPolyline: props.onCloseFillPolyline, 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, 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 () {
174
176
  props.nv.clearDrawing();
175
177
  props.resampleImage();
176
178
  props.setDrawingChanged(false);
@@ -0,0 +1,74 @@
1
+ /** @typedef {'polyline' | 'freehand'} PenDraftKind */
2
+ /**
3
+ * @typedef {Object} PenDraft
4
+ * @property {PenDraftKind} kind
5
+ * @property {Uint8Array} baseBitmap
6
+ * @property {number} axCorSag
7
+ * @property {number} penValue
8
+ * @property {[number, number, number][]} [vertices]
9
+ * @property {[number, number, number][]} [strokeVoxels]
10
+ * @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
11
+ */
12
+ export function isFreehandPenActive(nv: any): any;
13
+ 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
+ export function redrawPolylineDraft(nv: any, draft: any): void;
25
+ export function translatePolylineVertices(vertices: any, delta: any): any;
26
+ export function updatePolylineVertex(vertices: any, index: any, newVox: any): any;
27
+ export function syncPolylineDraftToNv(nv: any, draft: any): void;
28
+ export function captureFreehandDraft(nv: any, sessionStartBitmap: any, axCorSag: any): {
29
+ kind: string;
30
+ baseBitmap: Uint8Array;
31
+ axCorSag: any;
32
+ penValue: any;
33
+ strokeVoxels: number[][];
34
+ bounds: {
35
+ x1: number;
36
+ y1: number;
37
+ z1: number;
38
+ x2: number;
39
+ y2: number;
40
+ z2: number;
41
+ };
42
+ } | null;
43
+ export function redrawFreehandDraft(nv: any, draft: any): void;
44
+ export function translateFreehandDraft(draft: any, delta: any): any;
45
+ export function resizeFreehandDraft(draft: any, cornerIndex: any, newVox: any): any;
46
+ export function boundsToFreehandCorners(bounds: any, axCorSag: any): any[][];
47
+ export function polylineDraftFromNv(nv: any): {
48
+ kind: string;
49
+ vertices: any;
50
+ baseBitmap: Uint8Array;
51
+ axCorSag: any;
52
+ penValue: any;
53
+ } | null;
54
+ export function applyPenDraft(nv: any, draft: any, { fillClosed }?: {
55
+ fillClosed?: boolean | undefined;
56
+ }): void;
57
+ export function cancelPenDraft(nv: any, draft: any): void;
58
+ export type PenDraftKind = 'polyline' | 'freehand';
59
+ export type PenDraft = {
60
+ kind: PenDraftKind;
61
+ baseBitmap: Uint8Array;
62
+ axCorSag: number;
63
+ penValue: number;
64
+ vertices?: [number, number, number][] | undefined;
65
+ strokeVoxels?: [number, number, number][] | undefined;
66
+ bounds?: {
67
+ x1: number;
68
+ y1: number;
69
+ x2: number;
70
+ y2: number;
71
+ z1: number;
72
+ z2: number;
73
+ } | undefined;
74
+ };
@@ -0,0 +1,261 @@
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
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
13
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
14
+ if (ar || !(i in from)) {
15
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
16
+ ar[i] = from[i];
17
+ }
18
+ }
19
+ return to.concat(ar || Array.prototype.slice.call(from));
20
+ };
21
+ import { translatePt } from "./shapeDraftUtils";
22
+ import { NI_PEN_TYPE } from "./niivuePenType";
23
+ /** @typedef {'polyline' | 'freehand'} PenDraftKind */
24
+ /**
25
+ * @typedef {Object} PenDraft
26
+ * @property {PenDraftKind} kind
27
+ * @property {Uint8Array} baseBitmap
28
+ * @property {number} axCorSag
29
+ * @property {number} penValue
30
+ * @property {[number, number, number][]} [vertices]
31
+ * @property {[number, number, number][]} [strokeVoxels]
32
+ * @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
33
+ */
34
+ export function isFreehandPenActive(nv) {
35
+ return (nv.opts.isFilledPen &&
36
+ nv.opts.drawingEnabled &&
37
+ nv.opts.penType === NI_PEN_TYPE.PEN &&
38
+ !nv.opts.polylinePenMode);
39
+ }
40
+ export function shouldDeferFreehandCommit(nv) {
41
+ return !!nv.opts.deferFreehandCommit && isFreehandPenActive(nv);
42
+ }
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
+ export function redrawPolylineDraft(nv, draft) {
54
+ var _a;
55
+ if (!((_a = draft === null || draft === void 0 ? void 0 : draft.vertices) === null || _a === void 0 ? void 0 : _a.length) || !draft.baseBitmap)
56
+ return;
57
+ nv.drawBitmap.set(draft.baseBitmap);
58
+ nv.drawPenAxCorSag = draft.axCorSag;
59
+ for (var i = 1; i < draft.vertices.length; i++) {
60
+ nv.drawPenLine(draft.vertices[i], draft.vertices[i - 1], draft.penValue);
61
+ }
62
+ nv.refreshDrawing(false, false);
63
+ nv.drawScene();
64
+ }
65
+ export function translatePolylineVertices(vertices, delta) {
66
+ return vertices.map(function (v) { return translatePt(v, delta); });
67
+ }
68
+ export function updatePolylineVertex(vertices, index, newVox) {
69
+ var next = vertices.map(function (v) { return __spreadArray([], v, true); });
70
+ next[index] = __spreadArray([], newVox, true);
71
+ return next;
72
+ }
73
+ export function syncPolylineDraftToNv(nv, draft) {
74
+ nv._cloudMrPolylineVertices = draft.vertices.map(function (v) { return __spreadArray([], v, true); });
75
+ if (nv.drawBitmap) {
76
+ nv._cloudMrPolylineBaseBitmap = nv.drawBitmap.slice();
77
+ }
78
+ nv._cloudMrPolylineAxCorSag = draft.axCorSag;
79
+ }
80
+ export function captureFreehandDraft(nv, sessionStartBitmap, axCorSag) {
81
+ var _a;
82
+ if (!nv.drawBitmap || !sessionStartBitmap)
83
+ return null;
84
+ var penValue = nv.opts.penValue;
85
+ var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
86
+ if (!dims)
87
+ return null;
88
+ var strokeVoxels = [];
89
+ for (var i = 0; i < nv.drawBitmap.length; i++) {
90
+ if (nv.drawBitmap[i] === penValue && sessionStartBitmap[i] !== penValue) {
91
+ var z = Math.floor(i / (dims[1] * dims[2]));
92
+ var rem = i - z * dims[1] * dims[2];
93
+ var y = Math.floor(rem / dims[1]);
94
+ var x = rem % dims[1];
95
+ strokeVoxels.push([x, y, z]);
96
+ }
97
+ }
98
+ if (strokeVoxels.length === 0)
99
+ return null;
100
+ var x1 = Infinity;
101
+ var y1 = Infinity;
102
+ var z1 = Infinity;
103
+ var x2 = -Infinity;
104
+ var y2 = -Infinity;
105
+ var z2 = -Infinity;
106
+ for (var _i = 0, strokeVoxels_1 = strokeVoxels; _i < strokeVoxels_1.length; _i++) {
107
+ var _b = strokeVoxels_1[_i], x = _b[0], y = _b[1], z = _b[2];
108
+ x1 = Math.min(x1, x);
109
+ y1 = Math.min(y1, y);
110
+ z1 = Math.min(z1, z);
111
+ x2 = Math.max(x2, x);
112
+ y2 = Math.max(y2, y);
113
+ z2 = Math.max(z2, z);
114
+ }
115
+ return {
116
+ kind: "freehand",
117
+ baseBitmap: new Uint8Array(sessionStartBitmap),
118
+ axCorSag: axCorSag,
119
+ penValue: penValue,
120
+ strokeVoxels: strokeVoxels,
121
+ bounds: { x1: x1, y1: y1, z1: z1, x2: x2, y2: y2, z2: z2 }
122
+ };
123
+ }
124
+ export function redrawFreehandDraft(nv, draft) {
125
+ var _a;
126
+ if (!((_a = draft === null || draft === void 0 ? void 0 : draft.strokeVoxels) === null || _a === void 0 ? void 0 : _a.length) || !draft.baseBitmap)
127
+ return;
128
+ nv.drawBitmap.set(draft.baseBitmap);
129
+ for (var _i = 0, _b = draft.strokeVoxels; _i < _b.length; _i++) {
130
+ var _c = _b[_i], x = _c[0], y = _c[1], z = _c[2];
131
+ nv.drawPt(x, y, z, draft.penValue);
132
+ }
133
+ nv.refreshDrawing(false, false);
134
+ nv.drawScene();
135
+ }
136
+ export function translateFreehandDraft(draft, delta) {
137
+ return __assign(__assign({}, draft), { strokeVoxels: draft.strokeVoxels.map(function (v) { return translatePt(v, delta); }), bounds: draft.bounds
138
+ ? {
139
+ x1: draft.bounds.x1 + delta[0],
140
+ y1: draft.bounds.y1 + delta[1],
141
+ z1: draft.bounds.z1 + delta[2],
142
+ x2: draft.bounds.x2 + delta[0],
143
+ y2: draft.bounds.y2 + delta[1],
144
+ z2: draft.bounds.z2 + delta[2]
145
+ }
146
+ : draft.bounds });
147
+ }
148
+ export function resizeFreehandDraft(draft, cornerIndex, newVox) {
149
+ if (!draft.bounds)
150
+ return draft;
151
+ var axCorSag = draft.axCorSag;
152
+ var b = __assign({}, draft.bounds);
153
+ var corners = boundsToFreehandCorners(b, axCorSag);
154
+ corners[cornerIndex] = __spreadArray([], newVox, true);
155
+ var xs = corners.map(function (c) { return c[0]; });
156
+ var ys = corners.map(function (c) { return c[1]; });
157
+ var zs = corners.map(function (c) { return c[2]; });
158
+ var nextBounds = {
159
+ x1: Math.min.apply(Math, xs),
160
+ y1: Math.min.apply(Math, ys),
161
+ z1: Math.min.apply(Math, zs),
162
+ x2: Math.max.apply(Math, xs),
163
+ y2: Math.max.apply(Math, ys),
164
+ z2: Math.max.apply(Math, zs)
165
+ };
166
+ var oldW = Math.max(1, b.x2 - b.x1);
167
+ var oldH = Math.max(1, b.y2 - b.y1);
168
+ var newW = Math.max(1, nextBounds.x2 - nextBounds.x1);
169
+ var newH = Math.max(1, nextBounds.y2 - nextBounds.y1);
170
+ var scaled = draft.strokeVoxels.map(function (v) {
171
+ var relH = (v[0] - b.x1) / oldW;
172
+ var relV = (v[1] - b.y1) / oldH;
173
+ if (axCorSag === 0) {
174
+ return [
175
+ Math.round(nextBounds.x1 + relH * newW),
176
+ Math.round(nextBounds.y1 + relV * newH),
177
+ v[2],
178
+ ];
179
+ }
180
+ if (axCorSag === 1) {
181
+ return [
182
+ Math.round(nextBounds.x1 + relH * newW),
183
+ v[1],
184
+ Math.round(nextBounds.y1 + relV * newH),
185
+ ];
186
+ }
187
+ return [
188
+ v[0],
189
+ Math.round(nextBounds.x1 + relH * newW),
190
+ Math.round(nextBounds.y1 + relV * newH),
191
+ ];
192
+ });
193
+ return __assign(__assign({}, draft), { strokeVoxels: scaled, bounds: nextBounds });
194
+ }
195
+ export function boundsToFreehandCorners(bounds, axCorSag) {
196
+ var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
197
+ if (axCorSag === 0) {
198
+ var z = Math.round((z1 + z2) / 2);
199
+ return [
200
+ [x1, y1, z],
201
+ [x2, y1, z],
202
+ [x1, y2, z],
203
+ [x2, y2, z],
204
+ ];
205
+ }
206
+ if (axCorSag === 1) {
207
+ var y = Math.round((y1 + y2) / 2);
208
+ return [
209
+ [x1, y, z1],
210
+ [x2, y, z1],
211
+ [x1, y, z2],
212
+ [x2, y, z2],
213
+ ];
214
+ }
215
+ var x = Math.round((x1 + x2) / 2);
216
+ return [
217
+ [x, y1, z1],
218
+ [x, y2, z1],
219
+ [x, y1, z2],
220
+ [x, y2, z2],
221
+ ];
222
+ }
223
+ export function polylineDraftFromNv(nv) {
224
+ var vertices = nv._cloudMrPolylineVertices;
225
+ var baseBitmap = nv._cloudMrPolylineSessionStartBitmap;
226
+ if (!(vertices === null || vertices === void 0 ? void 0 : vertices.length) || vertices.length < 2 || !baseBitmap)
227
+ return null;
228
+ return {
229
+ kind: "polyline",
230
+ vertices: vertices.map(function (v) { return __spreadArray([], v, true); }),
231
+ baseBitmap: new Uint8Array(baseBitmap),
232
+ axCorSag: nv._cloudMrPolylineAxCorSag,
233
+ penValue: nv.opts.penValue
234
+ };
235
+ }
236
+ export function applyPenDraft(nv, draft, _a) {
237
+ var _b = _a === void 0 ? {} : _a, _c = _b.fillClosed, fillClosed = _c === void 0 ? false : _c;
238
+ if (draft.kind === "polyline") {
239
+ 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
+ }
247
+ else {
248
+ redrawFreehandDraft(nv, draft);
249
+ }
250
+ nv.drawAddUndoBitmap(nv.drawFillOverwrites);
251
+ if (typeof nv.onDrawingChanged === "function") {
252
+ nv.onDrawingChanged("draw");
253
+ }
254
+ }
255
+ export function cancelPenDraft(nv, draft) {
256
+ if (draft === null || draft === void 0 ? void 0 : draft.baseBitmap) {
257
+ nv.drawBitmap.set(draft.baseBitmap);
258
+ nv.refreshDrawing(true, false);
259
+ nv.drawScene();
260
+ }
261
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloudmr-ux",
3
- "version": "4.5.0",
3
+ "version": "4.5.2",
4
4
  "author": "erosmontin@gmail.com",
5
5
  "license": "MIT",
6
6
  "repository": "erosmontin/cloudmr-ux",