cloudmr-ux 4.6.5 → 4.6.7
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/CloudMrNiivueViewer.js +46 -28
- package/dist/CmrComponents/niivue-viewer/NiivuePatcher.js +25 -108
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.d.ts +2 -3
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.js +0 -1
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.d.ts +3 -1
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.js +28 -15
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/MroDrawToolkit.js +5 -0
- package/dist/CmrComponents/niivue-viewer/shapeDraftUtils.d.ts +0 -17
- package/dist/CmrComponents/niivue-viewer/shapeDraftUtils.js +10 -26
- package/package.json +1 -1
|
@@ -586,7 +586,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
586
586
|
setPenDraft(null);
|
|
587
587
|
penDraftRef.current = null;
|
|
588
588
|
nv._cloudMrPenDraftActive = false;
|
|
589
|
-
clearActivePenDraftRef();
|
|
590
589
|
setPolylineVertexCount(0);
|
|
591
590
|
}
|
|
592
591
|
if (shapeDraftRef.current) {
|
|
@@ -611,7 +610,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
611
610
|
setPenDraft(null);
|
|
612
611
|
penDraftRef.current = null;
|
|
613
612
|
nv._cloudMrPenDraftActive = false;
|
|
614
|
-
clearActivePenDraftRef();
|
|
615
613
|
}
|
|
616
614
|
setDrawShapeTool(null);
|
|
617
615
|
nv.opts.penType = NI_PEN_TYPE.PEN;
|
|
@@ -643,7 +641,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
643
641
|
setPenDraft(null);
|
|
644
642
|
penDraftRef.current = null;
|
|
645
643
|
nv._cloudMrPenDraftActive = false;
|
|
646
|
-
clearActivePenDraftRef();
|
|
647
644
|
}
|
|
648
645
|
nvUpdateDrawPen({ target: { value: 8 } });
|
|
649
646
|
nvSetDrawingEnabled(true);
|
|
@@ -835,18 +832,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
835
832
|
(_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
836
833
|
}
|
|
837
834
|
}
|
|
838
|
-
function syncActiveShapeDraftRef() {
|
|
839
|
-
nv._cloudMrActiveShapeDraft = shapeDraftRef.current;
|
|
840
|
-
}
|
|
841
|
-
function clearActiveShapeDraftRef() {
|
|
842
|
-
nv._cloudMrActiveShapeDraft = null;
|
|
843
|
-
}
|
|
844
|
-
function syncActivePenDraftRef() {
|
|
845
|
-
nv._cloudMrActivePenDraft = penDraftRef.current;
|
|
846
|
-
}
|
|
847
|
-
function clearActivePenDraftRef() {
|
|
848
|
-
nv._cloudMrActivePenDraft = null;
|
|
849
|
-
}
|
|
850
835
|
function cancelPenDraftHandler() {
|
|
851
836
|
var _a;
|
|
852
837
|
var draft = penDraftRef.current;
|
|
@@ -860,7 +845,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
860
845
|
setPenDraft(null);
|
|
861
846
|
penDraftRef.current = null;
|
|
862
847
|
nv._cloudMrPenDraftActive = false;
|
|
863
|
-
clearActivePenDraftRef();
|
|
864
848
|
if (drawShapeToolRef.current === "pen") {
|
|
865
849
|
nvSetDrawingEnabled(true);
|
|
866
850
|
}
|
|
@@ -878,7 +862,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
878
862
|
setPenDraft(null);
|
|
879
863
|
penDraftRef.current = null;
|
|
880
864
|
nv._cloudMrPenDraftActive = false;
|
|
881
|
-
clearActivePenDraftRef();
|
|
882
865
|
setDrawingChanged(true);
|
|
883
866
|
if (drawShapeToolRef.current === "pen") {
|
|
884
867
|
nvSetDrawingEnabled(true);
|
|
@@ -895,7 +878,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
895
878
|
function onPenDraftChange(draft) {
|
|
896
879
|
setPenDraft(draft);
|
|
897
880
|
penDraftRef.current = draft;
|
|
898
|
-
syncActivePenDraftRef();
|
|
899
881
|
if (draft.kind === "polyline") {
|
|
900
882
|
syncPolylineDraftToNv(nv, draft);
|
|
901
883
|
}
|
|
@@ -907,7 +889,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
907
889
|
setPenDraft(draft);
|
|
908
890
|
penDraftRef.current = draft;
|
|
909
891
|
nv._cloudMrPenDraftActive = true;
|
|
910
|
-
syncActivePenDraftRef();
|
|
911
892
|
nvSetDrawingEnabled(false);
|
|
912
893
|
};
|
|
913
894
|
nv.onPolylineChange = function (count) {
|
|
@@ -922,13 +903,11 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
922
903
|
if (draft) {
|
|
923
904
|
setPenDraft(draft);
|
|
924
905
|
penDraftRef.current = draft;
|
|
925
|
-
syncActivePenDraftRef();
|
|
926
906
|
}
|
|
927
907
|
}
|
|
928
908
|
else if (((_b = penDraftRef.current) === null || _b === void 0 ? void 0 : _b.kind) === "polyline") {
|
|
929
909
|
setPenDraft(null);
|
|
930
910
|
penDraftRef.current = null;
|
|
931
|
-
clearActivePenDraftRef();
|
|
932
911
|
}
|
|
933
912
|
};
|
|
934
913
|
function cancelShapeDraft() {
|
|
@@ -941,7 +920,6 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
941
920
|
setShapeDraft(null);
|
|
942
921
|
shapeDraftRef.current = null;
|
|
943
922
|
nv._cloudMrShapeDraftActive = false;
|
|
944
|
-
clearActiveShapeDraftRef();
|
|
945
923
|
if (drawShapeToolRef.current) {
|
|
946
924
|
nvSetDrawingEnabled(true);
|
|
947
925
|
}
|
|
@@ -954,22 +932,26 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
954
932
|
setShapeDraft(null);
|
|
955
933
|
shapeDraftRef.current = null;
|
|
956
934
|
nv._cloudMrShapeDraftActive = false;
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
935
|
+
// Deactivate tool entirely — palette closes, user re-enters edit by clicking the ROI
|
|
936
|
+
setDrawShapeTool(null);
|
|
937
|
+
nv.opts.deferShapeCommit = false;
|
|
938
|
+
nv.opts.penType = NI_PEN_TYPE.PEN;
|
|
939
|
+
nvSetDrawingEnabled(false);
|
|
961
940
|
resampleImage();
|
|
962
941
|
}
|
|
963
942
|
function onShapeDraftChange(draft) {
|
|
964
943
|
setShapeDraft(draft);
|
|
965
944
|
shapeDraftRef.current = draft;
|
|
966
|
-
syncActiveShapeDraftRef();
|
|
967
945
|
}
|
|
968
946
|
nv.onShapeDraftReady = function (draft) {
|
|
969
947
|
setShapeDraft(draft);
|
|
970
948
|
shapeDraftRef.current = draft;
|
|
971
949
|
nv._cloudMrShapeDraftActive = true;
|
|
972
|
-
|
|
950
|
+
// Auto-select the correct tool so the palette opens on re-entry via ROI click
|
|
951
|
+
var tool = draft.penType === NI_PEN_TYPE.ELLIPSE ? "ellipse" : "rectangle";
|
|
952
|
+
setDrawShapeTool(tool);
|
|
953
|
+
nv.opts.penType = draft.penType;
|
|
954
|
+
nv.opts.deferShapeCommit = true;
|
|
973
955
|
nvSetDrawingEnabled(false);
|
|
974
956
|
};
|
|
975
957
|
nv.onApplyActiveDraft = function () {
|
|
@@ -986,6 +968,32 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
986
968
|
React.useEffect(function () {
|
|
987
969
|
nv._cloudMrShapeDraftActive = shapeDraft != null;
|
|
988
970
|
}, [shapeDraft]);
|
|
971
|
+
React.useEffect(function () {
|
|
972
|
+
if (!shapeDraft && !penDraft) {
|
|
973
|
+
return undefined;
|
|
974
|
+
}
|
|
975
|
+
var canvas = document.getElementById("niiCanvas");
|
|
976
|
+
if (!canvas) {
|
|
977
|
+
return undefined;
|
|
978
|
+
}
|
|
979
|
+
var applyOnRightClick = function (event) {
|
|
980
|
+
var _a;
|
|
981
|
+
event.preventDefault();
|
|
982
|
+
event.stopPropagation();
|
|
983
|
+
(_a = nv.onApplyActiveDraft) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
984
|
+
};
|
|
985
|
+
var onMouseDown = function (event) {
|
|
986
|
+
if (event.button === 2) {
|
|
987
|
+
applyOnRightClick(event);
|
|
988
|
+
}
|
|
989
|
+
};
|
|
990
|
+
canvas.addEventListener("mousedown", onMouseDown, true);
|
|
991
|
+
canvas.addEventListener("contextmenu", applyOnRightClick, true);
|
|
992
|
+
return function () {
|
|
993
|
+
canvas.removeEventListener("mousedown", onMouseDown, true);
|
|
994
|
+
canvas.removeEventListener("contextmenu", applyOnRightClick, true);
|
|
995
|
+
};
|
|
996
|
+
}, [shapeDraft, penDraft]);
|
|
989
997
|
React.useEffect(function () {
|
|
990
998
|
if (!shapeDraft && !penDraft) {
|
|
991
999
|
return undefined;
|
|
@@ -999,6 +1007,16 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
999
1007
|
else if (penDraft) {
|
|
1000
1008
|
cancelPenDraftHandler();
|
|
1001
1009
|
}
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
if (event.key === "Enter") {
|
|
1013
|
+
event.preventDefault();
|
|
1014
|
+
if (shapeDraft) {
|
|
1015
|
+
applyShapeDraft();
|
|
1016
|
+
}
|
|
1017
|
+
else if (penDraft) {
|
|
1018
|
+
applyPenDraftHandler();
|
|
1019
|
+
}
|
|
1002
1020
|
}
|
|
1003
1021
|
};
|
|
1004
1022
|
window.addEventListener("keydown", onKeyDown);
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
captureDeferredShapeDraft,
|
|
7
7
|
captureShapeDraftFromClick,
|
|
8
8
|
isDraftTooSmall,
|
|
9
|
-
isVoxelPartOfDraft,
|
|
10
9
|
redrawDraftShape,
|
|
11
10
|
shouldDeferShapeCommit,
|
|
12
11
|
} from "./shapeDraftUtils.js";
|
|
@@ -1469,136 +1468,52 @@ Niivue.prototype.cloudMrResetPolyline = function cloudMrResetPolyline() {
|
|
|
1469
1468
|
resetPolylineState(this);
|
|
1470
1469
|
};
|
|
1471
1470
|
|
|
1471
|
+
const RIGHT_MOUSE_BUTTON = 2;
|
|
1472
|
+
|
|
1472
1473
|
function cloudMrHasApplyableDraft(nv) {
|
|
1473
1474
|
return !!(nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive);
|
|
1474
1475
|
}
|
|
1475
1476
|
|
|
1476
|
-
function
|
|
1477
|
-
|
|
1478
|
-
if (!draft || !nv._cloudMrShapeDraftActive) return false;
|
|
1479
|
-
const vox = voxFromMouse(nv);
|
|
1480
|
-
if (!vox) return false;
|
|
1481
|
-
return isVoxelPartOfDraft(nv, draft, vox);
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
function isClickOnActivePenDraft(nv) {
|
|
1485
|
-
const draft = nv._cloudMrActivePenDraft;
|
|
1486
|
-
if (!draft || !nv._cloudMrPenDraftActive) return false;
|
|
1487
|
-
const vox = voxFromMouse(nv);
|
|
1488
|
-
if (!vox) return false;
|
|
1489
|
-
return isVoxelPartOfDraft(nv, draft, vox);
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
function voxelLabelAt(nv, vox) {
|
|
1493
|
-
const dims = nv.back?.dims;
|
|
1494
|
-
if (!dims || !nv.drawBitmap || !vox) return 0;
|
|
1495
|
-
const dx = dims[1];
|
|
1496
|
-
const dy = dims[2];
|
|
1497
|
-
const idx = vox[0] + vox[1] * dx + vox[2] * dx * dy;
|
|
1498
|
-
return nv.drawBitmap[idx] || 0;
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
function canReopenShapeDraftOnClick(nv) {
|
|
1502
|
-
const penType = nv.opts.penType;
|
|
1503
|
-
return (
|
|
1504
|
-
nv.opts.deferShapeCommit &&
|
|
1505
|
-
nv.opts.drawingEnabled &&
|
|
1506
|
-
!nv._cloudMrShapeDraftActive &&
|
|
1507
|
-
!nv._cloudMrPenDraftActive &&
|
|
1508
|
-
(penType === NI_PEN_TYPE.RECTANGLE || penType === NI_PEN_TYPE.ELLIPSE)
|
|
1509
|
-
);
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
function cloudMrOpenShapeDraftFromClick(nv) {
|
|
1513
|
-
const reopenDraft = captureShapeDraftFromClick(nv);
|
|
1514
|
-
if (!reopenDraft) return false;
|
|
1515
|
-
redrawDraftShape(nv, reopenDraft);
|
|
1516
|
-
nv._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
1517
|
-
if (typeof nv.onShapeDraftReady === "function") {
|
|
1518
|
-
nv.onShapeDraftReady(reopenDraft);
|
|
1519
|
-
}
|
|
1520
|
-
return true;
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
/**
|
|
1524
|
-
* Re-enter rectangle/ellipse edit mode when clicking an existing shape ROI.
|
|
1525
|
-
*/
|
|
1526
|
-
function cloudMrTryOpenShapeDraftOnMouseDown(nv) {
|
|
1527
|
-
if (nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive) {
|
|
1528
|
-
return false;
|
|
1529
|
-
}
|
|
1530
|
-
if (!canReopenShapeDraftOnClick(nv)) {
|
|
1477
|
+
function cloudMrTryApplyDraftOnRightClick(nv, event) {
|
|
1478
|
+
if (!cloudMrHasApplyableDraft(nv)) {
|
|
1531
1479
|
return false;
|
|
1532
1480
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
return false;
|
|
1536
|
-
}
|
|
1537
|
-
return cloudMrOpenShapeDraftFromClick(nv);
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
/**
|
|
1541
|
-
* Click-away exits shape edit mode; clicking another shape selects it for editing.
|
|
1542
|
-
* Disconnected same-color shapes are distinguished by flood-fill from the click point.
|
|
1543
|
-
*/
|
|
1544
|
-
function cloudMrHandleShapeDraftClickOnMouseDown(nv) {
|
|
1545
|
-
if (!nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive) {
|
|
1546
|
-
return false;
|
|
1547
|
-
}
|
|
1548
|
-
if (!nv.opts.deferShapeCommit || !nv.opts.drawingEnabled) {
|
|
1549
|
-
return false;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
const vox = voxFromMouse(nv);
|
|
1553
|
-
if (!vox) {
|
|
1554
|
-
return false;
|
|
1555
|
-
}
|
|
1556
|
-
if (isClickOnActiveShapeDraft(nv)) {
|
|
1557
|
-
return false;
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1481
|
+
event.preventDefault();
|
|
1482
|
+
event.stopPropagation();
|
|
1560
1483
|
if (typeof nv.onApplyActiveDraft === "function") {
|
|
1561
1484
|
nv.onApplyActiveDraft();
|
|
1562
1485
|
}
|
|
1563
|
-
|
|
1564
|
-
if (voxelLabelAt(nv, vox) > 0) {
|
|
1565
|
-
cloudMrOpenShapeDraftFromClick(nv);
|
|
1566
|
-
}
|
|
1567
1486
|
return true;
|
|
1568
1487
|
}
|
|
1569
1488
|
|
|
1570
|
-
/**
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
if (!vox) {
|
|
1489
|
+
/**
|
|
1490
|
+
* Re-enter rectangle/ellipse edit mode when clicking an existing applied ROI.
|
|
1491
|
+
* Works regardless of which tool is currently active (or whether any tool is active),
|
|
1492
|
+
* so clicking an ROI after apply always re-enters edit mode.
|
|
1493
|
+
*/
|
|
1494
|
+
function cloudMrTryReopenShapeDraftOnClick(nv) {
|
|
1495
|
+
if (nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive) {
|
|
1578
1496
|
return false;
|
|
1579
1497
|
}
|
|
1580
|
-
if (
|
|
1498
|
+
if (!isClickWithoutDrag(nv.uiData)) {
|
|
1581
1499
|
return false;
|
|
1582
1500
|
}
|
|
1583
1501
|
|
|
1584
|
-
|
|
1585
|
-
|
|
1502
|
+
const reopenDraft = captureShapeDraftFromClick(nv);
|
|
1503
|
+
if (!reopenDraft) return false;
|
|
1504
|
+
|
|
1505
|
+
redrawDraftShape(nv, reopenDraft);
|
|
1506
|
+
nv._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
1507
|
+
if (typeof nv.onShapeDraftReady === "function") {
|
|
1508
|
+
nv.onShapeDraftReady(reopenDraft);
|
|
1586
1509
|
}
|
|
1587
1510
|
return true;
|
|
1588
1511
|
}
|
|
1589
1512
|
|
|
1590
1513
|
const _mouseDownListener = Niivue.prototype.mouseDownListener;
|
|
1591
1514
|
Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
|
|
1592
|
-
if (e.button ===
|
|
1593
|
-
|
|
1594
|
-
return;
|
|
1595
|
-
}
|
|
1596
|
-
if (cloudMrHandlePenDraftClickOnMouseDown(this)) {
|
|
1597
|
-
return;
|
|
1598
|
-
}
|
|
1599
|
-
if (cloudMrTryOpenShapeDraftOnMouseDown(this)) {
|
|
1600
|
-
return;
|
|
1601
|
-
}
|
|
1515
|
+
if (e.button === RIGHT_MOUSE_BUTTON && cloudMrTryApplyDraftOnRightClick(this, e)) {
|
|
1516
|
+
return;
|
|
1602
1517
|
}
|
|
1603
1518
|
if (shouldDeferFreehandCommit(this) && this.drawBitmap) {
|
|
1604
1519
|
this._cloudMrFreehandSessionStartBitmap = this.drawBitmap.slice();
|
|
@@ -1668,12 +1583,14 @@ Niivue.prototype.mouseUpListener = function cloudMrMouseUpListener() {
|
|
|
1668
1583
|
}
|
|
1669
1584
|
|
|
1670
1585
|
if (!pendingDraft?.baseBitmap) {
|
|
1586
|
+
cloudMrTryReopenShapeDraftOnClick(this);
|
|
1671
1587
|
return;
|
|
1672
1588
|
}
|
|
1673
1589
|
if (isDraftTooSmall(pendingDraft.ptA, pendingDraft.ptB)) {
|
|
1674
1590
|
this.drawBitmap.set(pendingDraft.baseBitmap);
|
|
1675
1591
|
this.refreshDrawing(true, false);
|
|
1676
1592
|
this.drawScene();
|
|
1593
|
+
cloudMrTryReopenShapeDraftOnClick(this);
|
|
1677
1594
|
return;
|
|
1678
1595
|
}
|
|
1679
1596
|
this._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Adjust handles for polyline (vertex drag) or freehand (move only) drafts.
|
|
3
|
-
* @param {{ nv: any, draft: any, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
4
3
|
*/
|
|
5
4
|
export function PenDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
|
|
6
5
|
nv: any;
|
|
7
6
|
draft: any;
|
|
8
|
-
onDraftChange:
|
|
9
|
-
overlayKey
|
|
7
|
+
onDraftChange: any;
|
|
8
|
+
overlayKey: any;
|
|
10
9
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
11
10
|
export default PenDraftOverlay;
|
|
@@ -29,7 +29,6 @@ function cloneFreehandDraft(draft) {
|
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* Adjust handles for polyline (vertex drag) or freehand (move only) drafts.
|
|
32
|
-
* @param {{ nv: any, draft: any, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
33
32
|
*/
|
|
34
33
|
export function PenDraftOverlay(_a) {
|
|
35
34
|
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onCancelPenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onCancelShapeDraft, }: {
|
|
1
|
+
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onApplyPenDraft, onCancelPenDraft, onFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
|
|
2
2
|
expanded: any;
|
|
3
3
|
updateDrawPen: any;
|
|
4
4
|
setDrawingEnabled: any;
|
|
@@ -7,10 +7,12 @@ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEna
|
|
|
7
7
|
onPenDrawModeChange: any;
|
|
8
8
|
polylineVertexCount?: number | undefined;
|
|
9
9
|
penDraftActive?: boolean | undefined;
|
|
10
|
+
onApplyPenDraft: any;
|
|
10
11
|
onCancelPenDraft: any;
|
|
11
12
|
onFillPenDraft: any;
|
|
12
13
|
brushSize?: number | undefined;
|
|
13
14
|
updateBrushSize: any;
|
|
14
15
|
shapeDraftActive?: boolean | undefined;
|
|
16
|
+
onApplyShapeDraft: any;
|
|
15
17
|
onCancelShapeDraft: any;
|
|
16
18
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -11,13 +11,12 @@ var __assign = (this && this.__assign) || function () {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
/**
|
|
14
|
-
* Pen palette adds freehand vs polyline mode; pen/shape drafts show Cancel while adjusting.
|
|
15
|
-
* Shapes are applied automatically on mouse release.
|
|
14
|
+
* Pen palette adds freehand vs polyline mode; pen/shape drafts show Apply/Cancel while adjusting.
|
|
16
15
|
*/
|
|
17
16
|
import { Stack, IconButton, Button, Tooltip, Typography } from "@mui/material";
|
|
18
17
|
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
|
18
|
+
import CheckIcon from "@mui/icons-material/Check";
|
|
19
19
|
import CloseIcon from "@mui/icons-material/Close";
|
|
20
|
-
import FormatColorFillIcon from "@mui/icons-material/FormatColorFill";
|
|
21
20
|
import { BrushSizeSlider } from "./BrushSizeSlider";
|
|
22
21
|
var FILLED_COLORS = [
|
|
23
22
|
{ sx: { color: "red" } },
|
|
@@ -38,7 +37,7 @@ var modeBtnSx = function (active) { return ({
|
|
|
38
37
|
px: 0.75
|
|
39
38
|
}); };
|
|
40
39
|
export default function DrawColorPlatte(_a) {
|
|
41
|
-
var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d, _e = _a.penDraftActive, penDraftActive = _e === void 0 ? false : _e, onCancelPenDraft = _a.onCancelPenDraft, onFillPenDraft = _a.onFillPenDraft, _f = _a.brushSize, brushSize = _f === void 0 ? 1 : _f, updateBrushSize = _a.updateBrushSize, _g = _a.shapeDraftActive, shapeDraftActive = _g === void 0 ? false : _g, onCancelShapeDraft = _a.onCancelShapeDraft;
|
|
40
|
+
var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d, _e = _a.penDraftActive, penDraftActive = _e === void 0 ? false : _e, onApplyPenDraft = _a.onApplyPenDraft, onCancelPenDraft = _a.onCancelPenDraft, onFillPenDraft = _a.onFillPenDraft, _f = _a.brushSize, brushSize = _f === void 0 ? 1 : _f, updateBrushSize = _a.updateBrushSize, _g = _a.shapeDraftActive, shapeDraftActive = _g === void 0 ? false : _g, onApplyShapeDraft = _a.onApplyShapeDraft, onCancelShapeDraft = _a.onCancelShapeDraft;
|
|
42
41
|
return (_jsxs(Stack, __assign({ style: {
|
|
43
42
|
position: "absolute",
|
|
44
43
|
top: "100%",
|
|
@@ -63,22 +62,36 @@ export default function DrawColorPlatte(_a) {
|
|
|
63
62
|
py: 0.25,
|
|
64
63
|
px: 0.75,
|
|
65
64
|
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
66
|
-
} }, { children: "Cancel" })) })), penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: "Fill interior (keeps outline editable until
|
|
65
|
+
} }, { children: "Cancel" })) })), _jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 1 }, { children: [penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: "Fill interior (keeps outline editable until Apply)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "fill polyline", onClick: function () { return onFillPenDraft === null || onFillPenDraft === void 0 ? void 0 : onFillPenDraft(); }, sx: {
|
|
66
|
+
color: "#c9a0e8",
|
|
67
|
+
fontSize: ACTION_FONT_SIZE,
|
|
68
|
+
textTransform: "none",
|
|
69
|
+
minWidth: 0,
|
|
70
|
+
py: 0.25,
|
|
71
|
+
px: 0.75
|
|
72
|
+
} }, { children: "Fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply shape (enter or right-click)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply pen draft", onClick: function () { return onApplyPenDraft === null || onApplyPenDraft === void 0 ? void 0 : onApplyPenDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
|
|
73
|
+
color: "#c9a0e8",
|
|
74
|
+
fontSize: ACTION_FONT_SIZE,
|
|
75
|
+
textTransform: "none",
|
|
76
|
+
minWidth: 0,
|
|
77
|
+
py: 0.25,
|
|
78
|
+
px: 0.75,
|
|
79
|
+
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
80
|
+
} }, { children: "Apply" })) }))] }))] }))), shapeDraftActive && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: [_jsx(Tooltip, __assign({ title: "Cancel shape (Esc)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "cancel shape", onClick: function () { return onCancelShapeDraft === null || onCancelShapeDraft === void 0 ? void 0 : onCancelShapeDraft(); }, startIcon: _jsx(CloseIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
|
|
81
|
+
color: "#ccc",
|
|
82
|
+
fontSize: ACTION_FONT_SIZE,
|
|
83
|
+
textTransform: "none",
|
|
84
|
+
minWidth: 0,
|
|
85
|
+
py: 0.25,
|
|
86
|
+
px: 0.75,
|
|
87
|
+
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
88
|
+
} }, { children: "Cancel" })) })), _jsx(Tooltip, __assign({ title: "Apply shape (enter or right-click)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply shape", onClick: function () { return onApplyShapeDraft === null || onApplyShapeDraft === void 0 ? void 0 : onApplyShapeDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
|
|
67
89
|
color: "#c9a0e8",
|
|
68
90
|
fontSize: ACTION_FONT_SIZE,
|
|
69
91
|
textTransform: "none",
|
|
70
92
|
minWidth: 0,
|
|
71
93
|
py: 0.25,
|
|
72
94
|
px: 0.75,
|
|
73
|
-
ml: "auto",
|
|
74
95
|
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
75
|
-
} }, { children: "
|
|
76
|
-
color: "#ccc",
|
|
77
|
-
fontSize: ACTION_FONT_SIZE,
|
|
78
|
-
textTransform: "none",
|
|
79
|
-
minWidth: 0,
|
|
80
|
-
py: 0.25,
|
|
81
|
-
px: 0.75,
|
|
82
|
-
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
83
|
-
} }, { children: "Cancel" })) })) })))] })));
|
|
96
|
+
} }, { children: "Apply" })) }))] })))] })));
|
|
84
97
|
}
|
|
@@ -71,6 +71,11 @@ 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
|
+
// Close palette when tool is deactivated programmatically (e.g. after Apply)
|
|
75
|
+
if (drawShapeTool === null && !props.shapeDraftActive && !props.penDraftActive) {
|
|
76
|
+
setExpandedOption("n");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
74
79
|
if (!props.shapeDraftActive && !props.penDraftActive)
|
|
75
80
|
return;
|
|
76
81
|
if (drawShapeTool === "rectangle")
|
|
@@ -49,27 +49,10 @@ export function captureDeferredShapeDraft(nv: any): {
|
|
|
49
49
|
baseBitmap: Uint8Array | null;
|
|
50
50
|
};
|
|
51
51
|
export function shouldDeferShapeCommit(nv: any): any;
|
|
52
|
-
/**
|
|
53
|
-
* Flood-fill a connected voxel cluster from a seed.
|
|
54
|
-
* @returns {{ label: number, visited: Set<number>, voxels: [number,number,number][], bounds: object } | null}
|
|
55
|
-
*/
|
|
56
|
-
export function floodFillClusterFromVox(nv: any, seedVox: any): {
|
|
57
|
-
label: number;
|
|
58
|
-
visited: Set<number>;
|
|
59
|
-
voxels: [number, number, number][];
|
|
60
|
-
bounds: object;
|
|
61
|
-
} | null;
|
|
62
|
-
/** True when a voxel belongs to the live draft overlay (drawn on top of baseBitmap). */
|
|
63
|
-
export function isVoxelPartOfDraft(nv: any, draft: any, seedVox: any): boolean;
|
|
64
|
-
export function eraseClusterFromBitmap(bitmap: any, visited: any): Uint8Array;
|
|
65
|
-
/** Guess rectangle vs ellipse from the filled voxel pattern, not the active tool. */
|
|
66
|
-
export function inferShapePenTypeFromCluster(cluster: any, axCorSag: any): 1 | 2;
|
|
67
52
|
/**
|
|
68
53
|
* When the user clicks on an existing filled ROI while the rectangle/ellipse tool
|
|
69
54
|
* is active (but no draft is currently open), flood-fill the clicked cluster to
|
|
70
55
|
* reconstruct a ShapeDraft so the bounding-box overlay reappears for re-editing.
|
|
71
|
-
*
|
|
72
|
-
* Returns null if the click didn't land on a labeled voxel.
|
|
73
56
|
*/
|
|
74
57
|
export function captureShapeDraftFromClick(nv: any): {
|
|
75
58
|
ptA: any[];
|
|
@@ -210,11 +210,18 @@ function inferAxCorSagFromBounds(x1, y1, z1, x2, y2, z2, fallback) {
|
|
|
210
210
|
return 2;
|
|
211
211
|
return fallback;
|
|
212
212
|
}
|
|
213
|
+
function sliceKey(axCorSag, x, y, z) {
|
|
214
|
+
if (axCorSag === 0)
|
|
215
|
+
return "".concat(x, ",").concat(y);
|
|
216
|
+
if (axCorSag === 1)
|
|
217
|
+
return "".concat(x, ",").concat(z);
|
|
218
|
+
return "".concat(y, ",").concat(z);
|
|
219
|
+
}
|
|
213
220
|
/**
|
|
214
221
|
* Flood-fill a connected voxel cluster from a seed.
|
|
215
222
|
* @returns {{ label: number, visited: Set<number>, voxels: [number,number,number][], bounds: object } | null}
|
|
216
223
|
*/
|
|
217
|
-
|
|
224
|
+
function floodFillClusterFromVox(nv, seedVox) {
|
|
218
225
|
var _a;
|
|
219
226
|
var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
|
|
220
227
|
if (!dims || !nv.drawBitmap || !seedVox)
|
|
@@ -274,36 +281,15 @@ export function floodFillClusterFromVox(nv, seedVox) {
|
|
|
274
281
|
bounds: { x1: x1, y1: y1, z1: z1, x2: x2, y2: y2, z2: z2 }
|
|
275
282
|
};
|
|
276
283
|
}
|
|
277
|
-
|
|
278
|
-
export function isVoxelPartOfDraft(nv, draft, seedVox) {
|
|
279
|
-
var _a;
|
|
280
|
-
if (!(draft === null || draft === void 0 ? void 0 : draft.baseBitmap) || !(nv === null || nv === void 0 ? void 0 : nv.drawBitmap) || !seedVox)
|
|
281
|
-
return false;
|
|
282
|
-
var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
|
|
283
|
-
if (!dims)
|
|
284
|
-
return false;
|
|
285
|
-
var dx = dims[1];
|
|
286
|
-
var dy = dims[2];
|
|
287
|
-
var idx = voxelIndex(seedVox[0], seedVox[1], seedVox[2], dx, dy);
|
|
288
|
-
return (nv.drawBitmap[idx] === draft.penValue &&
|
|
289
|
-
draft.baseBitmap[idx] !== draft.penValue);
|
|
290
|
-
}
|
|
291
|
-
export function eraseClusterFromBitmap(bitmap, visited) {
|
|
284
|
+
function eraseClusterFromBitmap(bitmap, visited) {
|
|
292
285
|
var next = new Uint8Array(bitmap);
|
|
293
286
|
visited.forEach(function (idx) {
|
|
294
287
|
next[idx] = 0;
|
|
295
288
|
});
|
|
296
289
|
return next;
|
|
297
290
|
}
|
|
298
|
-
function sliceKey(axCorSag, x, y, z) {
|
|
299
|
-
if (axCorSag === 0)
|
|
300
|
-
return "".concat(x, ",").concat(y);
|
|
301
|
-
if (axCorSag === 1)
|
|
302
|
-
return "".concat(x, ",").concat(z);
|
|
303
|
-
return "".concat(y, ",").concat(z);
|
|
304
|
-
}
|
|
305
291
|
/** Guess rectangle vs ellipse from the filled voxel pattern, not the active tool. */
|
|
306
|
-
|
|
292
|
+
function inferShapePenTypeFromCluster(cluster, axCorSag) {
|
|
307
293
|
var bounds = cluster.bounds, voxels = cluster.voxels;
|
|
308
294
|
var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
|
|
309
295
|
var filled = new Set();
|
|
@@ -358,8 +344,6 @@ export function inferShapePenTypeFromCluster(cluster, axCorSag) {
|
|
|
358
344
|
* When the user clicks on an existing filled ROI while the rectangle/ellipse tool
|
|
359
345
|
* is active (but no draft is currently open), flood-fill the clicked cluster to
|
|
360
346
|
* reconstruct a ShapeDraft so the bounding-box overlay reappears for re-editing.
|
|
361
|
-
*
|
|
362
|
-
* Returns null if the click didn't land on a labeled voxel.
|
|
363
347
|
*/
|
|
364
348
|
export function captureShapeDraftFromClick(nv) {
|
|
365
349
|
var seedVox = voxFromMouse(nv);
|