cloudmr-ux 4.6.2 → 4.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.js +1 -1
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivueViewer.js +20 -13
- package/dist/CmrComponents/niivue-viewer/NiivuePatcher.js +96 -68
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.d.ts +2 -3
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.js +2 -7
- package/dist/CmrComponents/niivue-viewer/ShapeDraftOverlay.d.ts +2 -3
- package/dist/CmrComponents/niivue-viewer/ShapeDraftOverlay.js +2 -7
- package/dist/CmrComponents/niivue-viewer/penDraftUtils.d.ts +0 -21
- package/dist/CmrComponents/niivue-viewer/penDraftUtils.js +1 -30
- package/dist/CmrComponents/niivue-viewer/shapeDraftUtils.d.ts +3 -1
- package/dist/CmrComponents/niivue-viewer/shapeDraftUtils.js +61 -1
- package/package.json +1 -1
|
@@ -186,7 +186,7 @@ export function CloudMrNiivuePanel(props) {
|
|
|
186
186
|
left: 0,
|
|
187
187
|
width: "100%",
|
|
188
188
|
height: "100%"
|
|
189
|
-
} }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange,
|
|
189
|
+
} }), 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: {
|
|
190
190
|
width: {
|
|
191
191
|
xs: "100%",
|
|
192
192
|
md: "35%"
|
|
@@ -586,6 +586,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
586
586
|
setPenDraft(null);
|
|
587
587
|
penDraftRef.current = null;
|
|
588
588
|
nv._cloudMrPenDraftActive = false;
|
|
589
|
+
clearActivePenDraftRef();
|
|
589
590
|
setPolylineVertexCount(0);
|
|
590
591
|
}
|
|
591
592
|
if (shapeDraftRef.current) {
|
|
@@ -610,6 +611,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
610
611
|
setPenDraft(null);
|
|
611
612
|
penDraftRef.current = null;
|
|
612
613
|
nv._cloudMrPenDraftActive = false;
|
|
614
|
+
clearActivePenDraftRef();
|
|
613
615
|
}
|
|
614
616
|
setDrawShapeTool(null);
|
|
615
617
|
nv.opts.penType = NI_PEN_TYPE.PEN;
|
|
@@ -641,6 +643,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
641
643
|
setPenDraft(null);
|
|
642
644
|
penDraftRef.current = null;
|
|
643
645
|
nv._cloudMrPenDraftActive = false;
|
|
646
|
+
clearActivePenDraftRef();
|
|
644
647
|
}
|
|
645
648
|
nvUpdateDrawPen({ target: { value: 8 } });
|
|
646
649
|
nvSetDrawingEnabled(true);
|
|
@@ -832,12 +835,16 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
832
835
|
(_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
833
836
|
}
|
|
834
837
|
}
|
|
835
|
-
function
|
|
838
|
+
function syncActiveShapeDraftRef() {
|
|
836
839
|
nv._cloudMrActiveShapeDraft = shapeDraftRef.current;
|
|
837
|
-
nv._cloudMrActivePenDraft = penDraftRef.current;
|
|
838
840
|
}
|
|
839
|
-
function
|
|
841
|
+
function clearActiveShapeDraftRef() {
|
|
840
842
|
nv._cloudMrActiveShapeDraft = null;
|
|
843
|
+
}
|
|
844
|
+
function syncActivePenDraftRef() {
|
|
845
|
+
nv._cloudMrActivePenDraft = penDraftRef.current;
|
|
846
|
+
}
|
|
847
|
+
function clearActivePenDraftRef() {
|
|
841
848
|
nv._cloudMrActivePenDraft = null;
|
|
842
849
|
}
|
|
843
850
|
function cancelPenDraftHandler() {
|
|
@@ -853,7 +860,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
853
860
|
setPenDraft(null);
|
|
854
861
|
penDraftRef.current = null;
|
|
855
862
|
nv._cloudMrPenDraftActive = false;
|
|
856
|
-
|
|
863
|
+
clearActivePenDraftRef();
|
|
857
864
|
if (drawShapeToolRef.current === "pen") {
|
|
858
865
|
nvSetDrawingEnabled(true);
|
|
859
866
|
}
|
|
@@ -871,7 +878,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
871
878
|
setPenDraft(null);
|
|
872
879
|
penDraftRef.current = null;
|
|
873
880
|
nv._cloudMrPenDraftActive = false;
|
|
874
|
-
|
|
881
|
+
clearActivePenDraftRef();
|
|
875
882
|
setDrawingChanged(true);
|
|
876
883
|
if (drawShapeToolRef.current === "pen") {
|
|
877
884
|
nvSetDrawingEnabled(true);
|
|
@@ -888,7 +895,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
888
895
|
function onPenDraftChange(draft) {
|
|
889
896
|
setPenDraft(draft);
|
|
890
897
|
penDraftRef.current = draft;
|
|
891
|
-
|
|
898
|
+
syncActivePenDraftRef();
|
|
892
899
|
if (draft.kind === "polyline") {
|
|
893
900
|
syncPolylineDraftToNv(nv, draft);
|
|
894
901
|
}
|
|
@@ -900,7 +907,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
900
907
|
setPenDraft(draft);
|
|
901
908
|
penDraftRef.current = draft;
|
|
902
909
|
nv._cloudMrPenDraftActive = true;
|
|
903
|
-
|
|
910
|
+
syncActivePenDraftRef();
|
|
904
911
|
nvSetDrawingEnabled(false);
|
|
905
912
|
};
|
|
906
913
|
nv.onPolylineChange = function (count) {
|
|
@@ -915,13 +922,13 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
915
922
|
if (draft) {
|
|
916
923
|
setPenDraft(draft);
|
|
917
924
|
penDraftRef.current = draft;
|
|
918
|
-
|
|
925
|
+
syncActivePenDraftRef();
|
|
919
926
|
}
|
|
920
927
|
}
|
|
921
928
|
else if (((_b = penDraftRef.current) === null || _b === void 0 ? void 0 : _b.kind) === "polyline") {
|
|
922
929
|
setPenDraft(null);
|
|
923
930
|
penDraftRef.current = null;
|
|
924
|
-
|
|
931
|
+
clearActivePenDraftRef();
|
|
925
932
|
}
|
|
926
933
|
};
|
|
927
934
|
function cancelShapeDraft() {
|
|
@@ -934,7 +941,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
934
941
|
setShapeDraft(null);
|
|
935
942
|
shapeDraftRef.current = null;
|
|
936
943
|
nv._cloudMrShapeDraftActive = false;
|
|
937
|
-
|
|
944
|
+
clearActiveShapeDraftRef();
|
|
938
945
|
if (drawShapeToolRef.current) {
|
|
939
946
|
nvSetDrawingEnabled(true);
|
|
940
947
|
}
|
|
@@ -947,7 +954,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
947
954
|
setShapeDraft(null);
|
|
948
955
|
shapeDraftRef.current = null;
|
|
949
956
|
nv._cloudMrShapeDraftActive = false;
|
|
950
|
-
|
|
957
|
+
clearActiveShapeDraftRef();
|
|
951
958
|
if (drawShapeToolRef.current) {
|
|
952
959
|
nvSetDrawingEnabled(true);
|
|
953
960
|
}
|
|
@@ -956,13 +963,13 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
956
963
|
function onShapeDraftChange(draft) {
|
|
957
964
|
setShapeDraft(draft);
|
|
958
965
|
shapeDraftRef.current = draft;
|
|
959
|
-
|
|
966
|
+
syncActiveShapeDraftRef();
|
|
960
967
|
}
|
|
961
968
|
nv.onShapeDraftReady = function (draft) {
|
|
962
969
|
setShapeDraft(draft);
|
|
963
970
|
shapeDraftRef.current = draft;
|
|
964
971
|
nv._cloudMrShapeDraftActive = true;
|
|
965
|
-
|
|
972
|
+
syncActiveShapeDraftRef();
|
|
966
973
|
nvSetDrawingEnabled(false);
|
|
967
974
|
};
|
|
968
975
|
nv.onApplyActiveDraft = function () {
|
|
@@ -23,9 +23,6 @@ import {
|
|
|
23
23
|
} from "./polylinePenUtils.js";
|
|
24
24
|
import {
|
|
25
25
|
captureFreehandDraft,
|
|
26
|
-
capturePenDraftFromClick,
|
|
27
|
-
isPenDrawToolActive,
|
|
28
|
-
redrawFreehandDraft,
|
|
29
26
|
shouldDeferFreehandCommit,
|
|
30
27
|
} from "./penDraftUtils.js";
|
|
31
28
|
|
|
@@ -1476,89 +1473,133 @@ function cloudMrHasApplyableDraft(nv) {
|
|
|
1476
1473
|
return !!(nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive);
|
|
1477
1474
|
}
|
|
1478
1475
|
|
|
1479
|
-
function
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
if (
|
|
1484
|
-
|
|
1485
|
-
}
|
|
1486
|
-
return null;
|
|
1476
|
+
function isClickOnActiveShapeDraft(nv) {
|
|
1477
|
+
const draft = nv._cloudMrActiveShapeDraft;
|
|
1478
|
+
if (!draft || !nv._cloudMrShapeDraftActive) return false;
|
|
1479
|
+
const vox = voxFromMouse(nv);
|
|
1480
|
+
if (!vox) return false;
|
|
1481
|
+
return isVoxelPartOfDraft(nv, draft, vox);
|
|
1487
1482
|
}
|
|
1488
1483
|
|
|
1489
|
-
function
|
|
1490
|
-
const draft =
|
|
1491
|
-
if (!draft) return false;
|
|
1484
|
+
function isClickOnActivePenDraft(nv) {
|
|
1485
|
+
const draft = nv._cloudMrActivePenDraft;
|
|
1486
|
+
if (!draft || !nv._cloudMrPenDraftActive) return false;
|
|
1492
1487
|
const vox = voxFromMouse(nv);
|
|
1493
1488
|
if (!vox) return false;
|
|
1494
1489
|
return isVoxelPartOfDraft(nv, draft, vox);
|
|
1495
1490
|
}
|
|
1496
1491
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
if (!
|
|
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) {
|
|
1500
1528
|
return false;
|
|
1501
1529
|
}
|
|
1502
|
-
if (
|
|
1530
|
+
if (!canReopenShapeDraftOnClick(nv)) {
|
|
1503
1531
|
return false;
|
|
1504
1532
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1533
|
+
const vox = voxFromMouse(nv);
|
|
1534
|
+
if (!vox || !voxelLabelAt(nv, vox)) {
|
|
1535
|
+
return false;
|
|
1508
1536
|
}
|
|
1509
|
-
return
|
|
1537
|
+
return cloudMrOpenShapeDraftFromClick(nv);
|
|
1510
1538
|
}
|
|
1511
1539
|
|
|
1512
1540
|
/**
|
|
1513
|
-
*
|
|
1514
|
-
*
|
|
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.
|
|
1515
1543
|
*/
|
|
1516
|
-
function
|
|
1517
|
-
if (
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
) {
|
|
1544
|
+
function cloudMrHandleShapeDraftClickOnMouseDown(nv) {
|
|
1545
|
+
if (!nv._cloudMrShapeDraftActive || nv._cloudMrPenDraftActive) {
|
|
1546
|
+
return false;
|
|
1547
|
+
}
|
|
1548
|
+
if (!nv.opts.deferShapeCommit || !nv.opts.drawingEnabled) {
|
|
1522
1549
|
return false;
|
|
1523
1550
|
}
|
|
1524
1551
|
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const isPenTool = isPenDrawToolActive(nv);
|
|
1531
|
-
|
|
1532
|
-
if (!isShapeTool && !isPenTool) {
|
|
1552
|
+
const vox = voxFromMouse(nv);
|
|
1553
|
+
if (!vox) {
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
if (isClickOnActiveShapeDraft(nv)) {
|
|
1533
1557
|
return false;
|
|
1534
1558
|
}
|
|
1535
1559
|
|
|
1536
|
-
if (
|
|
1537
|
-
|
|
1538
|
-
if (!reopenDraft) return false;
|
|
1539
|
-
redrawDraftShape(nv, reopenDraft);
|
|
1540
|
-
nv._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
1541
|
-
if (typeof nv.onShapeDraftReady === "function") {
|
|
1542
|
-
nv.onShapeDraftReady(reopenDraft);
|
|
1543
|
-
}
|
|
1544
|
-
return true;
|
|
1560
|
+
if (typeof nv.onApplyActiveDraft === "function") {
|
|
1561
|
+
nv.onApplyActiveDraft();
|
|
1545
1562
|
}
|
|
1546
1563
|
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
redrawFreehandDraft(nv, reopenDraft);
|
|
1550
|
-
nv._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
1551
|
-
if (typeof nv.onPenDraftReady === "function") {
|
|
1552
|
-
nv.onPenDraftReady(reopenDraft);
|
|
1564
|
+
if (voxelLabelAt(nv, vox) > 0) {
|
|
1565
|
+
cloudMrOpenShapeDraftFromClick(nv);
|
|
1553
1566
|
}
|
|
1554
|
-
|
|
1555
|
-
|
|
1567
|
+
return true;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
/** Click-away applies a pen draft without starting a new stroke. */
|
|
1571
|
+
function cloudMrHandlePenDraftClickOnMouseDown(nv) {
|
|
1572
|
+
if (!nv._cloudMrPenDraftActive) {
|
|
1573
|
+
return false;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
const vox = voxFromMouse(nv);
|
|
1577
|
+
if (!vox) {
|
|
1578
|
+
return false;
|
|
1579
|
+
}
|
|
1580
|
+
if (isClickOnActivePenDraft(nv)) {
|
|
1581
|
+
return false;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
if (typeof nv.onApplyActiveDraft === "function") {
|
|
1585
|
+
nv.onApplyActiveDraft();
|
|
1556
1586
|
}
|
|
1557
1587
|
return true;
|
|
1558
1588
|
}
|
|
1559
1589
|
|
|
1560
1590
|
const _mouseDownListener = Niivue.prototype.mouseDownListener;
|
|
1561
1591
|
Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
|
|
1592
|
+
if (e.button === 0) {
|
|
1593
|
+
if (cloudMrHandleShapeDraftClickOnMouseDown(this)) {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
if (cloudMrHandlePenDraftClickOnMouseDown(this)) {
|
|
1597
|
+
return;
|
|
1598
|
+
}
|
|
1599
|
+
if (cloudMrTryOpenShapeDraftOnMouseDown(this)) {
|
|
1600
|
+
return;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1562
1603
|
if (shouldDeferFreehandCommit(this) && this.drawBitmap) {
|
|
1563
1604
|
this._cloudMrFreehandSessionStartBitmap = this.drawBitmap.slice();
|
|
1564
1605
|
this._cloudMrFreehandAxCorSag = -1;
|
|
@@ -1622,30 +1663,17 @@ Niivue.prototype.mouseUpListener = function cloudMrMouseUpListener() {
|
|
|
1622
1663
|
_mouseUpListener.call(this);
|
|
1623
1664
|
|
|
1624
1665
|
if (polylineClick) {
|
|
1625
|
-
if (cloudMrTryReopenDraftOnClick(this)) return;
|
|
1626
1666
|
addPolylineVertex(this);
|
|
1627
1667
|
return;
|
|
1628
1668
|
}
|
|
1629
1669
|
|
|
1630
|
-
if (cloudMrHasApplyableDraft(this) && isClickWithoutDrag(this.uiData)) {
|
|
1631
|
-
if (cloudMrTryApplyDraftOnClickAway(this)) {
|
|
1632
|
-
cloudMrTryReopenDraftOnClick(this);
|
|
1633
|
-
return;
|
|
1634
|
-
}
|
|
1635
|
-
if (isClickOnActiveDraft(this)) {
|
|
1636
|
-
return;
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
|
|
1640
1670
|
if (!pendingDraft?.baseBitmap) {
|
|
1641
|
-
cloudMrTryReopenDraftOnClick(this);
|
|
1642
1671
|
return;
|
|
1643
1672
|
}
|
|
1644
1673
|
if (isDraftTooSmall(pendingDraft.ptA, pendingDraft.ptB)) {
|
|
1645
1674
|
this.drawBitmap.set(pendingDraft.baseBitmap);
|
|
1646
1675
|
this.refreshDrawing(true, false);
|
|
1647
1676
|
this.drawScene();
|
|
1648
|
-
cloudMrTryReopenDraftOnClick(this);
|
|
1649
1677
|
return;
|
|
1650
1678
|
}
|
|
1651
1679
|
this._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
@@ -1,12 +1,11 @@
|
|
|
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,
|
|
3
|
+
* @param {{ nv: any, draft: any, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
4
4
|
*/
|
|
5
|
-
export function PenDraftOverlay({ nv, draft, onDraftChange,
|
|
5
|
+
export function PenDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
|
|
6
6
|
nv: any;
|
|
7
7
|
draft: any;
|
|
8
8
|
onDraftChange: (d: any) => void;
|
|
9
|
-
onApplyDraft?: (() => void) | undefined;
|
|
10
9
|
overlayKey?: unknown;
|
|
11
10
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
12
11
|
export default PenDraftOverlay;
|
|
@@ -29,10 +29,10 @@ 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,
|
|
32
|
+
* @param {{ nv: any, draft: any, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
33
33
|
*/
|
|
34
34
|
export function PenDraftOverlay(_a) {
|
|
35
|
-
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange,
|
|
35
|
+
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
|
|
36
36
|
var dragRef = useRef(null);
|
|
37
37
|
var draftRef = useRef(draft);
|
|
38
38
|
draftRef.current = draft;
|
|
@@ -110,7 +110,6 @@ export function PenDraftOverlay(_a) {
|
|
|
110
110
|
onDraftChange(nextDraft);
|
|
111
111
|
}, [nv, onDraftChange]);
|
|
112
112
|
finishDragRef.current = function () {
|
|
113
|
-
var hadDrag = dragRef.current != null;
|
|
114
113
|
dragRef.current = null;
|
|
115
114
|
if (onPointerMoveRef.current) {
|
|
116
115
|
window.removeEventListener("pointermove", onPointerMoveRef.current);
|
|
@@ -118,10 +117,6 @@ export function PenDraftOverlay(_a) {
|
|
|
118
117
|
if (finishDragRef.current) {
|
|
119
118
|
window.removeEventListener("pointerup", finishDragRef.current);
|
|
120
119
|
}
|
|
121
|
-
// Auto-apply as soon as the user releases the handle after a move/resize
|
|
122
|
-
if (hadDrag && onApplyDraft) {
|
|
123
|
-
onApplyDraft();
|
|
124
|
-
}
|
|
125
120
|
};
|
|
126
121
|
onPointerMoveRef.current = function (event) {
|
|
127
122
|
var drag = dragRef.current;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Overlay handles for adjusting a rectangle/ellipse draft before commit.
|
|
3
|
-
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void,
|
|
3
|
+
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
4
4
|
*/
|
|
5
|
-
export function ShapeDraftOverlay({ nv, draft, onDraftChange,
|
|
5
|
+
export function ShapeDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
|
|
6
6
|
nv: any;
|
|
7
7
|
draft: import('./shapeDraftUtils').ShapeDraft;
|
|
8
8
|
onDraftChange: (d: any) => void;
|
|
9
|
-
onApplyDraft?: (() => void) | undefined;
|
|
10
9
|
overlayKey?: unknown;
|
|
11
10
|
}): import("react/jsx-runtime").JSX.Element | null;
|
|
12
11
|
export default ShapeDraftOverlay;
|
|
@@ -25,11 +25,11 @@ var HANDLE_SIZE = 10;
|
|
|
25
25
|
var ACCENT = "#580f8b";
|
|
26
26
|
/**
|
|
27
27
|
* Overlay handles for adjusting a rectangle/ellipse draft before commit.
|
|
28
|
-
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void,
|
|
28
|
+
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
29
29
|
*/
|
|
30
30
|
export function ShapeDraftOverlay(_a) {
|
|
31
31
|
var _b;
|
|
32
|
-
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange,
|
|
32
|
+
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
|
|
33
33
|
var dragRef = useRef(null);
|
|
34
34
|
var draftRef = useRef(draft);
|
|
35
35
|
draftRef.current = draft;
|
|
@@ -84,7 +84,6 @@ export function ShapeDraftOverlay(_a) {
|
|
|
84
84
|
onDraftChange(nextDraft);
|
|
85
85
|
}, [nv, onDraftChange]);
|
|
86
86
|
finishDragRef.current = function () {
|
|
87
|
-
var hadDrag = dragRef.current != null;
|
|
88
87
|
dragRef.current = null;
|
|
89
88
|
if (onPointerMoveRef.current) {
|
|
90
89
|
window.removeEventListener("pointermove", onPointerMoveRef.current);
|
|
@@ -92,10 +91,6 @@ export function ShapeDraftOverlay(_a) {
|
|
|
92
91
|
if (finishDragRef.current) {
|
|
93
92
|
window.removeEventListener("pointerup", finishDragRef.current);
|
|
94
93
|
}
|
|
95
|
-
// Auto-apply as soon as the user releases the handle after a move/resize
|
|
96
|
-
if (hadDrag && onApplyDraft) {
|
|
97
|
-
onApplyDraft();
|
|
98
|
-
}
|
|
99
94
|
};
|
|
100
95
|
onPointerMoveRef.current = function (event) {
|
|
101
96
|
var drag = dragRef.current;
|
|
@@ -13,27 +13,6 @@
|
|
|
13
13
|
export function isEraserActive(nv: any): any;
|
|
14
14
|
export function isFreehandPenActive(nv: any): any;
|
|
15
15
|
export function shouldDeferFreehandCommit(nv: any): any;
|
|
16
|
-
export function isPenDrawToolActive(nv: any): any;
|
|
17
|
-
/**
|
|
18
|
-
* Re-enter pen edit mode when clicking an existing freehand/polyline ROI.
|
|
19
|
-
* Reopens as a freehand draft (move via bounding box) since vertex data is
|
|
20
|
-
* not stored in the bitmap alone.
|
|
21
|
-
*/
|
|
22
|
-
export function capturePenDraftFromClick(nv: any): {
|
|
23
|
-
kind: string;
|
|
24
|
-
baseBitmap: Uint8Array;
|
|
25
|
-
axCorSag: any;
|
|
26
|
-
penValue: number;
|
|
27
|
-
strokeVoxels: [number, number, number][];
|
|
28
|
-
bounds: {
|
|
29
|
-
x1: any;
|
|
30
|
-
y1: any;
|
|
31
|
-
z1: any;
|
|
32
|
-
x2: any;
|
|
33
|
-
y2: any;
|
|
34
|
-
z2: any;
|
|
35
|
-
};
|
|
36
|
-
} | null;
|
|
37
16
|
export function redrawPolylineDraft(nv: any, draft: any): void;
|
|
38
17
|
export function translatePolylineVertices(vertices: any, delta: any): any;
|
|
39
18
|
export function updatePolylineVertex(vertices: any, index: any, newVox: any): any;
|
|
@@ -18,8 +18,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
18
18
|
}
|
|
19
19
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
20
|
};
|
|
21
|
-
import { translatePt
|
|
22
|
-
import { axCorSagFromMouse, voxFromMouse } from "./polylinePenUtils";
|
|
21
|
+
import { translatePt } from "./shapeDraftUtils";
|
|
23
22
|
import { NI_PEN_TYPE } from "./niivuePenType";
|
|
24
23
|
/** @typedef {'polyline' | 'freehand'} PenDraftKind */
|
|
25
24
|
/**
|
|
@@ -48,34 +47,6 @@ export function isFreehandPenActive(nv) {
|
|
|
48
47
|
export function shouldDeferFreehandCommit(nv) {
|
|
49
48
|
return !!nv.opts.deferFreehandCommit && isFreehandPenActive(nv);
|
|
50
49
|
}
|
|
51
|
-
export function isPenDrawToolActive(nv) {
|
|
52
|
-
return (nv.opts.drawingEnabled &&
|
|
53
|
-
nv.opts.penType === NI_PEN_TYPE.PEN &&
|
|
54
|
-
nv.opts.penValue > 0 &&
|
|
55
|
-
(nv.opts.deferFreehandCommit || nv.opts.polylinePenMode));
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Re-enter pen edit mode when clicking an existing freehand/polyline ROI.
|
|
59
|
-
* Reopens as a freehand draft (move via bounding box) since vertex data is
|
|
60
|
-
* not stored in the bitmap alone.
|
|
61
|
-
*/
|
|
62
|
-
export function capturePenDraftFromClick(nv) {
|
|
63
|
-
var seedVox = voxFromMouse(nv);
|
|
64
|
-
var cluster = floodFillClusterFromVox(nv, seedVox);
|
|
65
|
-
if (!cluster)
|
|
66
|
-
return null;
|
|
67
|
-
var label = cluster.label, visited = cluster.visited, voxels = cluster.voxels, bounds = cluster.bounds;
|
|
68
|
-
var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
|
|
69
|
-
var axCorSag = nv.drawPenAxCorSag >= 0 ? nv.drawPenAxCorSag : axCorSagFromMouse(nv);
|
|
70
|
-
return {
|
|
71
|
-
kind: "freehand",
|
|
72
|
-
baseBitmap: eraseClusterFromBitmap(nv.drawBitmap, visited),
|
|
73
|
-
axCorSag: axCorSag,
|
|
74
|
-
penValue: label,
|
|
75
|
-
strokeVoxels: voxels,
|
|
76
|
-
bounds: { x1: x1, y1: y1, z1: z1, x2: x2, y2: y2, z2: z2 }
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
50
|
export function redrawPolylineDraft(nv, draft) {
|
|
80
51
|
var _a;
|
|
81
52
|
if (!((_a = draft === null || draft === void 0 ? void 0 : draft.vertices) === null || _a === void 0 ? void 0 : _a.length) || !draft.baseBitmap)
|
|
@@ -62,6 +62,8 @@ export function floodFillClusterFromVox(nv: any, seedVox: any): {
|
|
|
62
62
|
/** True when a voxel belongs to the live draft overlay (drawn on top of baseBitmap). */
|
|
63
63
|
export function isVoxelPartOfDraft(nv: any, draft: any, seedVox: any): boolean;
|
|
64
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;
|
|
65
67
|
/**
|
|
66
68
|
* When the user clicks on an existing filled ROI while the rectangle/ellipse tool
|
|
67
69
|
* is active (but no draft is currently open), flood-fill the clicked cluster to
|
|
@@ -74,7 +76,7 @@ export function captureShapeDraftFromClick(nv: any): {
|
|
|
74
76
|
ptB: any[];
|
|
75
77
|
penValue: number;
|
|
76
78
|
axCorSag: number;
|
|
77
|
-
penType:
|
|
79
|
+
penType: 1 | 2;
|
|
78
80
|
baseBitmap: Uint8Array;
|
|
79
81
|
} | null;
|
|
80
82
|
export type ShapeDraftKind = 'rectangle' | 'ellipse';
|
|
@@ -295,6 +295,65 @@ export function eraseClusterFromBitmap(bitmap, visited) {
|
|
|
295
295
|
});
|
|
296
296
|
return next;
|
|
297
297
|
}
|
|
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
|
+
/** Guess rectangle vs ellipse from the filled voxel pattern, not the active tool. */
|
|
306
|
+
export function inferShapePenTypeFromCluster(cluster, axCorSag) {
|
|
307
|
+
var bounds = cluster.bounds, voxels = cluster.voxels;
|
|
308
|
+
var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
|
|
309
|
+
var filled = new Set();
|
|
310
|
+
for (var _i = 0, voxels_1 = voxels; _i < voxels_1.length; _i++) {
|
|
311
|
+
var _a = voxels_1[_i], x = _a[0], y = _a[1], z = _a[2];
|
|
312
|
+
filled.add(sliceKey(axCorSag, x, y, z));
|
|
313
|
+
}
|
|
314
|
+
var uMin;
|
|
315
|
+
var uMax;
|
|
316
|
+
var vMin;
|
|
317
|
+
var vMax;
|
|
318
|
+
if (axCorSag === 0) {
|
|
319
|
+
uMin = x1;
|
|
320
|
+
uMax = x2;
|
|
321
|
+
vMin = y1;
|
|
322
|
+
vMax = y2;
|
|
323
|
+
}
|
|
324
|
+
else if (axCorSag === 1) {
|
|
325
|
+
uMin = x1;
|
|
326
|
+
uMax = x2;
|
|
327
|
+
vMin = z1;
|
|
328
|
+
vMax = z2;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
uMin = y1;
|
|
332
|
+
uMax = y2;
|
|
333
|
+
vMin = z1;
|
|
334
|
+
vMax = z2;
|
|
335
|
+
}
|
|
336
|
+
if (uMax <= uMin || vMax <= vMin) {
|
|
337
|
+
return NI_PEN_TYPE.RECTANGLE;
|
|
338
|
+
}
|
|
339
|
+
var cu = (uMin + uMax) / 2;
|
|
340
|
+
var cv = (vMin + vMax) / 2;
|
|
341
|
+
var ru = Math.max(0.5, (uMax - uMin) / 2);
|
|
342
|
+
var rv = Math.max(0.5, (vMax - vMin) / 2);
|
|
343
|
+
var rectScore = 0;
|
|
344
|
+
var ellipseScore = 0;
|
|
345
|
+
for (var u = uMin; u <= uMax; u++) {
|
|
346
|
+
for (var v = vMin; v <= vMax; v++) {
|
|
347
|
+
var has = filled.has("".concat(u, ",").concat(v));
|
|
348
|
+
var inEllipse = Math.pow(((u - cu) / ru), 2) + Math.pow(((v - cv) / rv), 2) <= 1.05;
|
|
349
|
+
if (has)
|
|
350
|
+
rectScore++;
|
|
351
|
+
if (has === inEllipse)
|
|
352
|
+
ellipseScore++;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return ellipseScore > rectScore ? NI_PEN_TYPE.ELLIPSE : NI_PEN_TYPE.RECTANGLE;
|
|
356
|
+
}
|
|
298
357
|
/**
|
|
299
358
|
* When the user clicks on an existing filled ROI while the rectangle/ellipse tool
|
|
300
359
|
* is active (but no draft is currently open), flood-fill the clicked cluster to
|
|
@@ -311,12 +370,13 @@ export function captureShapeDraftFromClick(nv) {
|
|
|
311
370
|
var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
|
|
312
371
|
var baseBitmap = eraseClusterFromBitmap(nv.drawBitmap, visited);
|
|
313
372
|
var axCorSag = inferAxCorSagFromBounds(x1, y1, z1, x2, y2, z2, nv.drawPenAxCorSag >= 0 ? nv.drawPenAxCorSag : 0);
|
|
373
|
+
var penType = inferShapePenTypeFromCluster(cluster, axCorSag);
|
|
314
374
|
return {
|
|
315
375
|
ptA: [x1, y1, z1],
|
|
316
376
|
ptB: [x2, y2, z2],
|
|
317
377
|
penValue: label,
|
|
318
378
|
axCorSag: axCorSag,
|
|
319
|
-
penType:
|
|
379
|
+
penType: penType,
|
|
320
380
|
baseBitmap: baseBitmap
|
|
321
381
|
};
|
|
322
382
|
}
|