cloudmr-ux 4.4.7 → 4.4.9
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.
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.d.ts +11 -1
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.js +4 -0
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivueViewer.js +76 -24
- package/dist/CmrComponents/niivue-viewer/NiivuePatcher.js +58 -0
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.d.ts +8 -1
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.js +38 -6
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/MroDrawToolkit.js +1 -1
- package/dist/CmrComponents/niivue-viewer/polylinePenUtils.d.ts +12 -0
- package/dist/CmrComponents/niivue-viewer/polylinePenUtils.js +123 -0
- package/package.json +1 -1
|
@@ -2,7 +2,17 @@ import React from "react";
|
|
|
2
2
|
import "./Toolbar.css";
|
|
3
3
|
import type { DrawToolkitProps } from "../draw-toolkit/DrawToolkit";
|
|
4
4
|
/** Props for {@link DrawToolkit} minus brush controls when using {@link MroDrawToolkit}. */
|
|
5
|
-
export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "brushSize" | "updateBrushSize"
|
|
5
|
+
export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "brushSize" | "updateBrushSize"> & {
|
|
6
|
+
penDrawMode?: "freehand" | "polyline";
|
|
7
|
+
onPenDrawModeChange?: (mode: "freehand" | "polyline") => void;
|
|
8
|
+
polylineVertexCount?: number;
|
|
9
|
+
onCancelPolyline?: () => void;
|
|
10
|
+
onFinishPolyline?: () => void;
|
|
11
|
+
onCloseFillPolyline?: () => void;
|
|
12
|
+
shapeDraftActive?: boolean;
|
|
13
|
+
onApplyShapeDraft?: () => void;
|
|
14
|
+
onCancelShapeDraft?: () => void;
|
|
15
|
+
};
|
|
6
16
|
export interface CloudMrNiivuePanelProps {
|
|
7
17
|
nv: any;
|
|
8
18
|
pipelineID: string;
|
|
@@ -106,9 +106,13 @@ export function CloudMrNiivuePanel(props) {
|
|
|
106
106
|
props.resampleImage();
|
|
107
107
|
}, [histogram]);
|
|
108
108
|
function applyDrawShapeTool(tool) {
|
|
109
|
+
var _a, _b;
|
|
109
110
|
if (props.shapeDraft) {
|
|
110
111
|
props.onCancelShapeDraft();
|
|
111
112
|
}
|
|
113
|
+
if (tool !== "pen") {
|
|
114
|
+
(_b = (_a = props.drawToolkitProps).onPenDrawModeChange) === null || _b === void 0 ? void 0 : _b.call(_a, "freehand");
|
|
115
|
+
}
|
|
112
116
|
props.setDrawShapeTool(tool);
|
|
113
117
|
var nv = props.nv;
|
|
114
118
|
nv.opts.deferShapeCommit = tool === "rectangle" || tool === "ellipse";
|
|
@@ -169,6 +169,8 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
169
169
|
var _21 = useState(false), saving = _21[0], setSaving = _21[1];
|
|
170
170
|
var _22 = useState(null), drawShapeTool = _22[0], setDrawShapeTool = _22[1];
|
|
171
171
|
var _23 = useState(null), shapeDraft = _23[0], setShapeDraft = _23[1];
|
|
172
|
+
var _24 = useState("freehand"), penDrawMode = _24[0], setPenDrawMode = _24[1];
|
|
173
|
+
var _25 = useState(0), polylineVertexCount = _25[0], setPolylineVertexCount = _25[1];
|
|
172
174
|
var shapeDraftRef = React.useRef(null);
|
|
173
175
|
var drawShapeToolRef = React.useRef(null);
|
|
174
176
|
drawShapeToolRef.current = drawShapeTool;
|
|
@@ -176,8 +178,8 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
176
178
|
React.useEffect(function () {
|
|
177
179
|
nv.opts.penBounds = 0;
|
|
178
180
|
}, []);
|
|
179
|
-
var
|
|
180
|
-
var
|
|
181
|
+
var _26 = React.useState(1.0), gamma = _26[0], setGamma = _26[1];
|
|
182
|
+
var _27 = React.useState(0), gammaKey = _27[0], setGammaKey = _27[1];
|
|
181
183
|
// Niivue → React bridge so other places (Toolbar) can force the UI to reset
|
|
182
184
|
nv.onResetGamma = function () {
|
|
183
185
|
setGamma(1.0);
|
|
@@ -222,13 +224,13 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
222
224
|
// setLayers([...nv.volumes])
|
|
223
225
|
// }, [])
|
|
224
226
|
// values dualslider
|
|
225
|
-
var
|
|
227
|
+
var _28 = useState(0), rangeKey = _28[0], setRangeKey = _28[1];
|
|
226
228
|
nv.onResetContrast = function () {
|
|
227
229
|
setRangeKey(rangeKey + 1);
|
|
228
230
|
};
|
|
229
|
-
var
|
|
230
|
-
var
|
|
231
|
-
var
|
|
231
|
+
var _29 = useState([0, 0, 0]), boundMins = _29[0], setBoundMins = _29[1];
|
|
232
|
+
var _30 = useState([1, 1, 1]), boundMaxs = _30[0], setBoundMaxs = _30[1];
|
|
233
|
+
var _31 = useState([0.5, 0.5, 0.5]), mms = _31[0], setMMs = _31[1];
|
|
232
234
|
nv.onImageLoaded = function () {
|
|
233
235
|
var _a, _b;
|
|
234
236
|
var oldCrosshairPos = __spreadArray([], nv.scene.crosshairPos, true);
|
|
@@ -511,7 +513,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
511
513
|
setTextsVisible(true);
|
|
512
514
|
}
|
|
513
515
|
}
|
|
514
|
-
var
|
|
516
|
+
var _32 = useState("pan"), dragMode = _32[0], setDragMode = _32[1];
|
|
515
517
|
function nvSetDragMode(dragMode) {
|
|
516
518
|
switch (dragMode) {
|
|
517
519
|
case "none":
|
|
@@ -720,7 +722,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
720
722
|
nv.opts.crosshairWidth = w;
|
|
721
723
|
nv.drawScene();
|
|
722
724
|
}
|
|
723
|
-
var
|
|
725
|
+
var _33 = useState({}), labelMapping = _33[0], setLabelMapping = _33[1];
|
|
724
726
|
function resampleImage(mapping) {
|
|
725
727
|
if (mapping === void 0) { mapping = labelMapping; }
|
|
726
728
|
var el = typeof document !== "undefined" ? document.getElementById("histoplot") : null;
|
|
@@ -733,6 +735,34 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
733
735
|
setROIs(next);
|
|
734
736
|
}
|
|
735
737
|
}
|
|
738
|
+
function syncPenDrawMode(mode) {
|
|
739
|
+
var _a;
|
|
740
|
+
setPenDrawMode(mode);
|
|
741
|
+
nv.opts.polylinePenMode = mode === "polyline";
|
|
742
|
+
nv.opts.isFilledPen = mode === "freehand";
|
|
743
|
+
if (mode === "freehand") {
|
|
744
|
+
(_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
function cancelPolylineDraft() {
|
|
748
|
+
var _a;
|
|
749
|
+
(_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
750
|
+
}
|
|
751
|
+
function finishPolylineOpen() {
|
|
752
|
+
if (nv.cloudMrFinishPolyline(false)) {
|
|
753
|
+
setDrawingChanged(true);
|
|
754
|
+
resampleImage();
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function finishPolylineClosed() {
|
|
758
|
+
if (nv.cloudMrFinishPolyline(true)) {
|
|
759
|
+
setDrawingChanged(true);
|
|
760
|
+
resampleImage();
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
nv.onPolylineChange = function (count) {
|
|
764
|
+
setPolylineVertexCount(count);
|
|
765
|
+
};
|
|
736
766
|
function cancelShapeDraft() {
|
|
737
767
|
var draft = shapeDraftRef.current;
|
|
738
768
|
if (!draft)
|
|
@@ -771,26 +801,38 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
771
801
|
nvSetDrawingEnabled(false);
|
|
772
802
|
};
|
|
773
803
|
React.useEffect(function () {
|
|
774
|
-
if (!shapeDraft)
|
|
804
|
+
if (!shapeDraft && !(penDrawMode === "polyline" && polylineVertexCount >= 2)) {
|
|
775
805
|
return undefined;
|
|
806
|
+
}
|
|
776
807
|
var onKeyDown = function (event) {
|
|
777
|
-
if (event.key === "
|
|
808
|
+
if (event.key === "Escape") {
|
|
778
809
|
event.preventDefault();
|
|
779
|
-
|
|
810
|
+
if (shapeDraft) {
|
|
811
|
+
cancelShapeDraft();
|
|
812
|
+
}
|
|
813
|
+
else if (polylineVertexCount >= 2) {
|
|
814
|
+
cancelPolylineDraft();
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
780
817
|
}
|
|
781
|
-
|
|
818
|
+
if (event.key === "Enter") {
|
|
782
819
|
event.preventDefault();
|
|
783
|
-
|
|
820
|
+
if (shapeDraft) {
|
|
821
|
+
applyShapeDraft();
|
|
822
|
+
}
|
|
823
|
+
else if (penDrawMode === "polyline" && polylineVertexCount >= 2) {
|
|
824
|
+
finishPolylineOpen();
|
|
825
|
+
}
|
|
784
826
|
}
|
|
785
827
|
};
|
|
786
828
|
window.addEventListener("keydown", onKeyDown);
|
|
787
829
|
return function () { return window.removeEventListener("keydown", onKeyDown); };
|
|
788
|
-
}, [shapeDraft]);
|
|
830
|
+
}, [shapeDraft, penDrawMode, polylineVertexCount]);
|
|
789
831
|
function nvUpdateSelectionBoxColor(rgb01) {
|
|
790
832
|
setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
|
|
791
833
|
nv.setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
|
|
792
834
|
}
|
|
793
|
-
var
|
|
835
|
+
var _34 = React.useState('axial'), sliceType = _34[0], setSliceType = _34[1];
|
|
794
836
|
function nvUpdateSliceType(newSliceType) {
|
|
795
837
|
setSliceType(newSliceType);
|
|
796
838
|
if (newSliceType === 'axial') {
|
|
@@ -930,14 +972,14 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
930
972
|
return [2 /*return*/];
|
|
931
973
|
});
|
|
932
974
|
}); };
|
|
933
|
-
var
|
|
934
|
-
var
|
|
935
|
-
var
|
|
936
|
-
}), saveConfirmCallback =
|
|
937
|
-
var
|
|
938
|
-
var
|
|
939
|
-
var
|
|
940
|
-
var
|
|
975
|
+
var _35 = useState(''), selectedROI = _35[0], setSelectedDrawingLayer = _35[1];
|
|
976
|
+
var _36 = useState(false), saveDialogOpen = _36[0], setSaveDialogOpen = _36[1];
|
|
977
|
+
var _37 = useState(function () {
|
|
978
|
+
}), saveConfirmCallback = _37[0], setSaveConfirmCallback = _37[1];
|
|
979
|
+
var _38 = useState(false), confirmationOpen = _38[0], setConfirmationOpen = _38[1];
|
|
980
|
+
var _39 = useState(function () { }), warningConfirmationCallback = _39[0], setWarningConfirmationCallback = _39[1];
|
|
981
|
+
var _40 = useState(function () { }), warningCancelCallback = _40[0], setWarningCancelCallback = _40[1];
|
|
982
|
+
var _41 = useState(false), drawingChanged = _41[0], setDrawingChanged = _41[1];
|
|
941
983
|
// When new drawing strokes are made on top of a saved ROI, reset the dropdown
|
|
942
984
|
// so it shows the "ROI Layer" placeholder instead of the stale saved name.
|
|
943
985
|
React.useEffect(function () {
|
|
@@ -1264,6 +1306,10 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1264
1306
|
cancelShapeDraft();
|
|
1265
1307
|
return;
|
|
1266
1308
|
}
|
|
1309
|
+
if (polylineVertexCount > 0) {
|
|
1310
|
+
cancelPolylineDraft();
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1267
1313
|
nv.drawUndo();
|
|
1268
1314
|
resampleImage();
|
|
1269
1315
|
if (nv.drawBitmap && nv.drawBitmap.every(function (v) { return v === 0; })) {
|
|
@@ -1275,7 +1321,13 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1275
1321
|
toggleROIVisible: toggleROIVisible,
|
|
1276
1322
|
drawingOpacity: drawingOpacity,
|
|
1277
1323
|
setDrawingOpacity: nvUpdateDrawingOpacity,
|
|
1278
|
-
setDrawingChanged: setDrawingChanged
|
|
1324
|
+
setDrawingChanged: setDrawingChanged,
|
|
1325
|
+
penDrawMode: penDrawMode,
|
|
1326
|
+
onPenDrawModeChange: syncPenDrawMode,
|
|
1327
|
+
polylineVertexCount: polylineVertexCount,
|
|
1328
|
+
onCancelPolyline: cancelPolylineDraft,
|
|
1329
|
+
onFinishPolyline: finishPolylineOpen,
|
|
1330
|
+
onCloseFillPolyline: finishPolylineClosed
|
|
1279
1331
|
};
|
|
1280
1332
|
return (_jsxs(Box, __assign({ sx: {
|
|
1281
1333
|
display: 'flex',
|
|
@@ -7,6 +7,15 @@ import {
|
|
|
7
7
|
isDraftTooSmall,
|
|
8
8
|
shouldDeferShapeCommit,
|
|
9
9
|
} from "./shapeDraftUtils.js";
|
|
10
|
+
import {
|
|
11
|
+
addPolylineVertex,
|
|
12
|
+
cancelPolyline,
|
|
13
|
+
finishPolyline,
|
|
14
|
+
isClickWithoutDrag,
|
|
15
|
+
isPolylinePenActive,
|
|
16
|
+
previewPolylineSegment,
|
|
17
|
+
resetPolylineState,
|
|
18
|
+
} from "./polylinePenUtils.js";
|
|
10
19
|
|
|
11
20
|
/*
|
|
12
21
|
* bitmapOverlay — in-browser state only: remembers each voxel's label *before* Group
|
|
@@ -1414,14 +1423,63 @@ Niivue.prototype.drawAddUndoBitmap = function cloudMrDrawAddUndoBitmap(...args)
|
|
|
1414
1423
|
return _drawAddUndoBitmap.apply(this, args);
|
|
1415
1424
|
};
|
|
1416
1425
|
|
|
1426
|
+
Niivue.prototype.cloudMrCancelPolyline = function cloudMrCancelPolyline() {
|
|
1427
|
+
cancelPolyline(this);
|
|
1428
|
+
};
|
|
1429
|
+
|
|
1430
|
+
Niivue.prototype.cloudMrFinishPolyline = function cloudMrFinishPolyline(fillClosed = false) {
|
|
1431
|
+
return finishPolyline(this, { fillClosed });
|
|
1432
|
+
};
|
|
1433
|
+
|
|
1434
|
+
Niivue.prototype.cloudMrResetPolyline = function cloudMrResetPolyline() {
|
|
1435
|
+
resetPolylineState(this);
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
const _mouseClick = Niivue.prototype.mouseClick;
|
|
1439
|
+
Niivue.prototype.mouseClick = function cloudMrMouseClick(...args) {
|
|
1440
|
+
if (isPolylinePenActive(this)) {
|
|
1441
|
+
if (this._cloudMrPolylineVertices?.length > 0) {
|
|
1442
|
+
previewPolylineSegment(this);
|
|
1443
|
+
}
|
|
1444
|
+
return false;
|
|
1445
|
+
}
|
|
1446
|
+
return _mouseClick.apply(this, args);
|
|
1447
|
+
};
|
|
1448
|
+
|
|
1449
|
+
const _mouseMoveListener = Niivue.prototype.mouseMoveListener;
|
|
1450
|
+
Niivue.prototype.mouseMoveListener = function cloudMrMouseMoveListener(event) {
|
|
1451
|
+
const result = _mouseMoveListener.call(this, event);
|
|
1452
|
+
if (isPolylinePenActive(this) && this._cloudMrPolylineVertices?.length > 0) {
|
|
1453
|
+
previewPolylineSegment(this);
|
|
1454
|
+
}
|
|
1455
|
+
return result;
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1417
1458
|
const _mouseUpListener = Niivue.prototype.mouseUpListener;
|
|
1418
1459
|
Niivue.prototype.mouseUpListener = function cloudMrMouseUpListener() {
|
|
1419
1460
|
let pendingDraft = null;
|
|
1461
|
+
let polylineClick = false;
|
|
1462
|
+
|
|
1420
1463
|
if (shouldDeferShapeCommit(this)) {
|
|
1421
1464
|
this._cloudMrSkipNextUndoBitmap = true;
|
|
1422
1465
|
pendingDraft = captureDeferredShapeDraft(this);
|
|
1423
1466
|
}
|
|
1467
|
+
|
|
1468
|
+
if (isPolylinePenActive(this) && isClickWithoutDrag(this.uiData)) {
|
|
1469
|
+
this.drawPenLocation = [NaN, NaN, NaN];
|
|
1470
|
+
this.drawPenFillPts = [];
|
|
1471
|
+
this._cloudMrSkipNextUndoBitmap = true;
|
|
1472
|
+
this._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
1473
|
+
polylineClick = true;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1424
1476
|
_mouseUpListener.call(this);
|
|
1477
|
+
|
|
1478
|
+
if (polylineClick) {
|
|
1479
|
+
addPolylineVertex(this);
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1425
1483
|
if (!pendingDraft?.baseBitmap) {
|
|
1426
1484
|
return;
|
|
1427
1485
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
|
|
1
|
+
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, onCancelPolyline, onFinishPolyline, onCloseFillPolyline, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
|
|
2
2
|
expanded: any;
|
|
3
3
|
updateDrawPen: any;
|
|
4
4
|
setDrawingEnabled: any;
|
|
5
|
+
showPenModes?: boolean | undefined;
|
|
6
|
+
penDrawMode?: string | undefined;
|
|
7
|
+
onPenDrawModeChange: any;
|
|
8
|
+
polylineVertexCount?: number | undefined;
|
|
9
|
+
onCancelPolyline: any;
|
|
10
|
+
onFinishPolyline: any;
|
|
11
|
+
onCloseFillPolyline: any;
|
|
5
12
|
shapeDraftActive?: boolean | undefined;
|
|
6
13
|
onApplyShapeDraft: any;
|
|
7
14
|
onCancelShapeDraft: any;
|
|
@@ -12,9 +12,9 @@ var __assign = (this && this.__assign) || function () {
|
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
/**
|
|
14
14
|
* Shared ROI label color row (NiiVue pen indices 1–6). Used by pen, rectangle, and ellipse tools.
|
|
15
|
-
*
|
|
15
|
+
* Pen palette adds freehand vs straight-line mode; rectangle/ellipse show Apply/Cancel while adjusting.
|
|
16
16
|
*/
|
|
17
|
-
import { Stack, IconButton, Button, Tooltip } from "@mui/material";
|
|
17
|
+
import { Stack, IconButton, Button, Tooltip, Typography } from "@mui/material";
|
|
18
18
|
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
|
19
19
|
import CheckIcon from "@mui/icons-material/Check";
|
|
20
20
|
import CloseIcon from "@mui/icons-material/Close";
|
|
@@ -28,24 +28,56 @@ var FILLED_COLORS = [
|
|
|
28
28
|
];
|
|
29
29
|
var ACTION_FONT_SIZE = "0.75rem";
|
|
30
30
|
var ACTION_ICON_SIZE = "0.875rem";
|
|
31
|
+
var modeBtnSx = function (active) { return ({
|
|
32
|
+
color: active ? "#c9a0e8" : "#bbb",
|
|
33
|
+
fontSize: ACTION_FONT_SIZE,
|
|
34
|
+
textTransform: "none",
|
|
35
|
+
minWidth: 0,
|
|
36
|
+
py: 0.25,
|
|
37
|
+
px: 0.75
|
|
38
|
+
}); };
|
|
31
39
|
export default function DrawColorPlatte(_a) {
|
|
32
|
-
var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _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, onCancelPolyline = _a.onCancelPolyline, onFinishPolyline = _a.onFinishPolyline, onCloseFillPolyline = _a.onCloseFillPolyline, _e = _a.shapeDraftActive, shapeDraftActive = _e === void 0 ? false : _e, onApplyShapeDraft = _a.onApplyShapeDraft, onCancelShapeDraft = _a.onCancelShapeDraft;
|
|
33
41
|
return (_jsxs(Stack, __assign({ style: {
|
|
34
42
|
position: "absolute",
|
|
35
43
|
top: "100%",
|
|
36
44
|
left: 0,
|
|
37
45
|
zIndex: 1500,
|
|
38
46
|
border: "".concat(expanded ? "1px" : 0, " solid #bbb"),
|
|
39
|
-
maxWidth: expanded ?
|
|
47
|
+
maxWidth: expanded ? 320 : 0,
|
|
48
|
+
minWidth: expanded ? 240 : 0,
|
|
40
49
|
overflow: expanded ? "visible" : "hidden",
|
|
41
50
|
borderRadius: "16px",
|
|
42
51
|
borderTopLeftRadius: "6pt",
|
|
43
52
|
borderTopRightRadius: "6pt",
|
|
44
53
|
background: "#333"
|
|
45
|
-
}, direction: "column", spacing: 0.5, sx: { py:
|
|
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: "Straight lines" }))] }))), _jsx(Stack, __assign({ direction: "row" }, { children: FILLED_COLORS.map(function (color, index) { return (_jsx(IconButton, __assign({ onClick: function () {
|
|
46
55
|
updateDrawPen({ target: { value: index + 1 } });
|
|
47
56
|
setDrawingEnabled(true);
|
|
48
|
-
} }, { children: _jsx(FiberManualRecordIcon, { sx: color.sx }) }), index)); }) })),
|
|
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 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: {
|
|
58
|
+
color: "#ccc",
|
|
59
|
+
fontSize: ACTION_FONT_SIZE,
|
|
60
|
+
textTransform: "none",
|
|
61
|
+
minWidth: 0,
|
|
62
|
+
py: 0.25,
|
|
63
|
+
px: 0.75,
|
|
64
|
+
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
65
|
+
} }, { 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: {
|
|
66
|
+
color: "#c9a0e8",
|
|
67
|
+
fontSize: ACTION_FONT_SIZE,
|
|
68
|
+
textTransform: "none",
|
|
69
|
+
minWidth: 0,
|
|
70
|
+
py: 0.25,
|
|
71
|
+
px: 0.75
|
|
72
|
+
} }, { 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: {
|
|
73
|
+
color: "#c9a0e8",
|
|
74
|
+
fontSize: ACTION_FONT_SIZE,
|
|
75
|
+
textTransform: "none",
|
|
76
|
+
minWidth: 0,
|
|
77
|
+
py: 0.25,
|
|
78
|
+
px: 0.75,
|
|
79
|
+
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
80
|
+
} }, { children: "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: {
|
|
49
81
|
color: "#ccc",
|
|
50
82
|
fontSize: ACTION_FONT_SIZE,
|
|
51
83
|
textTransform: "none",
|
|
@@ -170,7 +170,7 @@ export function MroDrawToolkit(props) {
|
|
|
170
170
|
alignItems: "center",
|
|
171
171
|
gap: 4,
|
|
172
172
|
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 })] })), _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 })] })), _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 () {
|
|
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 })] })), _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 })] })), _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
174
|
props.nv.clearDrawing();
|
|
175
175
|
props.resampleImage();
|
|
176
176
|
props.setDrawingChanged(false);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function isPolylinePenActive(nv: any): any;
|
|
2
|
+
export function isClickWithoutDrag(uiData: any, thresholdPx?: number): boolean;
|
|
3
|
+
export function axCorSagFromMouse(nv: any): any;
|
|
4
|
+
export function voxFromMouse(nv: any): any[] | null;
|
|
5
|
+
export function notifyPolylineChange(nv: any, count: any): void;
|
|
6
|
+
export function resetPolylineState(nv: any): void;
|
|
7
|
+
export function cancelPolyline(nv: any): void;
|
|
8
|
+
export function previewPolylineSegment(nv: any): void;
|
|
9
|
+
export function addPolylineVertex(nv: any): boolean;
|
|
10
|
+
export function finishPolyline(nv: any, { fillClosed }?: {
|
|
11
|
+
fillClosed?: boolean | undefined;
|
|
12
|
+
}): boolean;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
2
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
3
|
+
if (ar || !(i in from)) {
|
|
4
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
5
|
+
ar[i] = from[i];
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
9
|
+
};
|
|
10
|
+
import { NI_PEN_TYPE } from "./niivuePenType";
|
|
11
|
+
var CLICK_DRAG_PX = 5;
|
|
12
|
+
export function isPolylinePenActive(nv) {
|
|
13
|
+
return (nv.opts.polylinePenMode &&
|
|
14
|
+
nv.opts.drawingEnabled &&
|
|
15
|
+
nv.opts.penType === NI_PEN_TYPE.PEN);
|
|
16
|
+
}
|
|
17
|
+
export function isClickWithoutDrag(uiData, thresholdPx) {
|
|
18
|
+
if (thresholdPx === void 0) { thresholdPx = CLICK_DRAG_PX; }
|
|
19
|
+
var dx = uiData.dragStart[0] - uiData.dragEnd[0];
|
|
20
|
+
var dy = uiData.dragStart[1] - uiData.dragEnd[1];
|
|
21
|
+
return dx * dx + dy * dy <= thresholdPx * thresholdPx;
|
|
22
|
+
}
|
|
23
|
+
export function axCorSagFromMouse(nv) {
|
|
24
|
+
var _a;
|
|
25
|
+
var tile = nv.tileIndex(nv.mousePos[0], nv.mousePos[1]);
|
|
26
|
+
if (tile >= 0 && ((_a = nv.screenSlices) === null || _a === void 0 ? void 0 : _a[tile])) {
|
|
27
|
+
return nv.screenSlices[tile].axCorSag;
|
|
28
|
+
}
|
|
29
|
+
return nv.drawPenAxCorSag >= 0 ? nv.drawPenAxCorSag : 0;
|
|
30
|
+
}
|
|
31
|
+
export function voxFromMouse(nv) {
|
|
32
|
+
var frac = nv.canvasPos2frac(nv.mousePos);
|
|
33
|
+
if (!frac || frac[0] < 0)
|
|
34
|
+
return null;
|
|
35
|
+
var vox = nv.frac2vox(frac);
|
|
36
|
+
return [vox[0], vox[1], vox[2]];
|
|
37
|
+
}
|
|
38
|
+
export function notifyPolylineChange(nv, count) {
|
|
39
|
+
if (typeof nv.onPolylineChange === "function") {
|
|
40
|
+
nv.onPolylineChange(count);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function resetPolylineState(nv) {
|
|
44
|
+
nv._cloudMrPolylineVertices = [];
|
|
45
|
+
nv._cloudMrPolylineBaseBitmap = null;
|
|
46
|
+
nv._cloudMrPolylineSessionStartBitmap = null;
|
|
47
|
+
nv._cloudMrPolylineAxCorSag = -1;
|
|
48
|
+
notifyPolylineChange(nv, 0);
|
|
49
|
+
}
|
|
50
|
+
export function cancelPolyline(nv) {
|
|
51
|
+
if (nv._cloudMrPolylineSessionStartBitmap) {
|
|
52
|
+
nv.drawBitmap.set(nv._cloudMrPolylineSessionStartBitmap);
|
|
53
|
+
nv.refreshDrawing(true, false);
|
|
54
|
+
nv.drawScene();
|
|
55
|
+
}
|
|
56
|
+
resetPolylineState(nv);
|
|
57
|
+
}
|
|
58
|
+
export function previewPolylineSegment(nv) {
|
|
59
|
+
var verts = nv._cloudMrPolylineVertices;
|
|
60
|
+
if (!(verts === null || verts === void 0 ? void 0 : verts.length) || !nv._cloudMrPolylineBaseBitmap)
|
|
61
|
+
return;
|
|
62
|
+
var cursor = voxFromMouse(nv);
|
|
63
|
+
if (!cursor)
|
|
64
|
+
return;
|
|
65
|
+
nv.drawBitmap.set(nv._cloudMrPolylineBaseBitmap);
|
|
66
|
+
nv.drawPenAxCorSag = nv._cloudMrPolylineAxCorSag;
|
|
67
|
+
nv.drawPenLine(cursor, verts[verts.length - 1], nv.opts.penValue);
|
|
68
|
+
nv.refreshDrawing(false, false);
|
|
69
|
+
nv.drawScene();
|
|
70
|
+
}
|
|
71
|
+
export function addPolylineVertex(nv) {
|
|
72
|
+
var pt = voxFromMouse(nv);
|
|
73
|
+
if (!pt)
|
|
74
|
+
return false;
|
|
75
|
+
var penValue = nv.opts.penValue;
|
|
76
|
+
var verts = nv._cloudMrPolylineVertices || (nv._cloudMrPolylineVertices = []);
|
|
77
|
+
if (verts.length === 0) {
|
|
78
|
+
nv._cloudMrPolylineSessionStartBitmap = nv.drawBitmap.slice();
|
|
79
|
+
nv._cloudMrPolylineBaseBitmap = nv._cloudMrPolylineSessionStartBitmap.slice();
|
|
80
|
+
nv._cloudMrPolylineAxCorSag = axCorSagFromMouse(nv);
|
|
81
|
+
nv.drawPenAxCorSag = nv._cloudMrPolylineAxCorSag;
|
|
82
|
+
verts.push(pt);
|
|
83
|
+
nv.drawPt(pt[0], pt[1], pt[2], penValue);
|
|
84
|
+
nv.refreshDrawing(false, false);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
var prev = verts[verts.length - 1];
|
|
88
|
+
if (prev[0] === pt[0] && prev[1] === pt[1] && prev[2] === pt[2]) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
nv.drawPenAxCorSag = nv._cloudMrPolylineAxCorSag;
|
|
92
|
+
nv.drawPenLine(pt, prev, penValue);
|
|
93
|
+
verts.push(pt);
|
|
94
|
+
nv._cloudMrPolylineBaseBitmap = nv.drawBitmap.slice();
|
|
95
|
+
nv.refreshDrawing(false, false);
|
|
96
|
+
}
|
|
97
|
+
nv.drawScene();
|
|
98
|
+
notifyPolylineChange(nv, verts.length);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
export function finishPolyline(nv, _a) {
|
|
102
|
+
var _b = _a === void 0 ? {} : _a, _c = _b.fillClosed, fillClosed = _c === void 0 ? false : _c;
|
|
103
|
+
var verts = nv._cloudMrPolylineVertices;
|
|
104
|
+
if (!verts || verts.length < 2) {
|
|
105
|
+
resetPolylineState(nv);
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
if (fillClosed && verts.length >= 3) {
|
|
109
|
+
var first = verts[0];
|
|
110
|
+
var last = verts[verts.length - 1];
|
|
111
|
+
nv.drawPenAxCorSag = nv._cloudMrPolylineAxCorSag;
|
|
112
|
+
nv.drawPenLine(first, last, nv.opts.penValue);
|
|
113
|
+
nv.drawPenFillPts = verts.map(function (v) { return __spreadArray([], v, true); });
|
|
114
|
+
nv.drawPenFilled();
|
|
115
|
+
}
|
|
116
|
+
nv.drawAddUndoBitmap(nv.drawFillOverwrites);
|
|
117
|
+
if (typeof nv.onDrawingChanged === "function") {
|
|
118
|
+
nv.onDrawingChanged("draw");
|
|
119
|
+
}
|
|
120
|
+
resetPolylineState(nv);
|
|
121
|
+
nv.drawScene();
|
|
122
|
+
return true;
|
|
123
|
+
}
|