cloudmr-ux 4.4.9 → 4.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.d.ts +25 -4
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.js +11 -2
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivueViewer.js +108 -39
- package/dist/CmrComponents/niivue-viewer/NiivuePatcher.js +45 -0
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.d.ts +10 -0
- package/dist/CmrComponents/niivue-viewer/PenDraftOverlay.js +201 -0
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/BrushSizeSlider.d.ts +5 -0
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/BrushSizeSlider.js +36 -0
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.d.ts +7 -4
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/DrawColorPlatte.js +8 -8
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/EraserPlatte.d.ts +7 -2
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/EraserPlatte.js +20 -36
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/MroDrawToolkit.js +6 -4
- package/dist/CmrComponents/niivue-viewer/penDraftUtils.d.ts +74 -0
- package/dist/CmrComponents/niivue-viewer/penDraftUtils.js +261 -0
- package/package.json +1 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import "./Toolbar.css";
|
|
3
3
|
import type { DrawToolkitProps } from "../draw-toolkit/DrawToolkit";
|
|
4
|
-
/** Props for {@link
|
|
5
|
-
export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "
|
|
4
|
+
/** Props for {@link MroDrawToolkit} — extends draw toolkit with MRO pen/shape controls. */
|
|
5
|
+
export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "rois" | "selectedROI" | "setSelectedROI" | "saveROI" | "labelsVisible" | "toggleLabelsVisible"> & {
|
|
6
6
|
penDrawMode?: "freehand" | "polyline";
|
|
7
7
|
onPenDrawModeChange?: (mode: "freehand" | "polyline") => void;
|
|
8
8
|
polylineVertexCount?: number;
|
|
9
9
|
onCancelPolyline?: () => void;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
onApplyPenDraft?: () => void;
|
|
11
|
+
onCancelPenDraft?: () => void;
|
|
12
|
+
onCloseFillPenDraft?: () => void;
|
|
13
|
+
penDraftActive?: boolean;
|
|
12
14
|
shapeDraftActive?: boolean;
|
|
13
15
|
onApplyShapeDraft?: () => void;
|
|
14
16
|
onCancelShapeDraft?: () => void;
|
|
@@ -48,5 +50,24 @@ export interface CloudMrNiivuePanelProps {
|
|
|
48
50
|
onShapeDraftChange: (draft: import("./shapeDraftUtils").ShapeDraft) => void;
|
|
49
51
|
onApplyShapeDraft: () => void;
|
|
50
52
|
onCancelShapeDraft: () => void;
|
|
53
|
+
penDraft: {
|
|
54
|
+
kind: "polyline" | "freehand";
|
|
55
|
+
baseBitmap: Uint8Array;
|
|
56
|
+
axCorSag: number;
|
|
57
|
+
penValue: number;
|
|
58
|
+
vertices?: [number, number, number][];
|
|
59
|
+
strokeVoxels?: [number, number, number][];
|
|
60
|
+
bounds?: {
|
|
61
|
+
x1: number;
|
|
62
|
+
y1: number;
|
|
63
|
+
x2: number;
|
|
64
|
+
y2: number;
|
|
65
|
+
z1: number;
|
|
66
|
+
z2: number;
|
|
67
|
+
};
|
|
68
|
+
} | null;
|
|
69
|
+
onPenDraftChange: (draft: NonNullable<CloudMrNiivuePanelProps["penDraft"]>) => void;
|
|
70
|
+
onApplyPenDraft: () => void;
|
|
71
|
+
onCancelPenDraft: () => void;
|
|
51
72
|
}
|
|
52
73
|
export declare function CloudMrNiivuePanel(props: CloudMrNiivuePanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -56,6 +56,7 @@ import { NiivueContrastAdjustments } from "../niivue-contrast-adjustments/Niivue
|
|
|
56
56
|
import { NiivueRoiTable } from "../niivue-roi-table/NiivueRoiTable";
|
|
57
57
|
import { MroDrawToolkit } from "./mro-draw-toolkit/MroDrawToolkit";
|
|
58
58
|
import { ShapeDraftOverlay } from "./ShapeDraftOverlay";
|
|
59
|
+
import { PenDraftOverlay } from "./PenDraftOverlay";
|
|
59
60
|
export function CloudMrNiivuePanel(props) {
|
|
60
61
|
var _this = this;
|
|
61
62
|
var canvas = React.useRef(null);
|
|
@@ -110,12 +111,17 @@ export function CloudMrNiivuePanel(props) {
|
|
|
110
111
|
if (props.shapeDraft) {
|
|
111
112
|
props.onCancelShapeDraft();
|
|
112
113
|
}
|
|
114
|
+
if (props.penDraft) {
|
|
115
|
+
props.onCancelPenDraft();
|
|
116
|
+
}
|
|
113
117
|
if (tool !== "pen") {
|
|
114
118
|
(_b = (_a = props.drawToolkitProps).onPenDrawModeChange) === null || _b === void 0 ? void 0 : _b.call(_a, "freehand");
|
|
115
119
|
}
|
|
116
120
|
props.setDrawShapeTool(tool);
|
|
117
121
|
var nv = props.nv;
|
|
118
122
|
nv.opts.deferShapeCommit = tool === "rectangle" || tool === "ellipse";
|
|
123
|
+
nv.opts.deferFreehandCommit =
|
|
124
|
+
tool === "pen" && props.drawToolkitProps.penDrawMode === "freehand";
|
|
119
125
|
nv.opts.penType =
|
|
120
126
|
tool === "rectangle"
|
|
121
127
|
? NI_PEN_TYPE.RECTANGLE
|
|
@@ -150,8 +156,11 @@ export function CloudMrNiivuePanel(props) {
|
|
|
150
156
|
if (props.shapeDraft) {
|
|
151
157
|
props.onCancelShapeDraft();
|
|
152
158
|
}
|
|
159
|
+
if (props.penDraft) {
|
|
160
|
+
props.onCancelPenDraft();
|
|
161
|
+
}
|
|
153
162
|
props.setDrawShapeTool(null);
|
|
154
|
-
}, shapeDraftActive: props.shapeDraft != null, onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft, style: {
|
|
163
|
+
}, shapeDraftActive: props.shapeDraft != null, penDraftActive: props.penDraft != null, onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft, style: {
|
|
155
164
|
marginBottom: 0,
|
|
156
165
|
width: "100%",
|
|
157
166
|
flexShrink: 0
|
|
@@ -182,7 +191,7 @@ export function CloudMrNiivuePanel(props) {
|
|
|
182
191
|
left: 0,
|
|
183
192
|
width: "100%",
|
|
184
193
|
height: "100%"
|
|
185
|
-
} }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange, overlayKey: props.mms }))] }))] })), _jsxs(Box, __assign({ sx: {
|
|
194
|
+
} }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange, overlayKey: props.mms })), props.penDraft && (_jsx(PenDraftOverlay, { nv: props.nv, draft: props.penDraft, onDraftChange: props.onPenDraftChange, overlayKey: props.mms }))] }))] })), _jsxs(Box, __assign({ sx: {
|
|
186
195
|
width: {
|
|
187
196
|
xs: "100%",
|
|
188
197
|
md: "35%"
|
|
@@ -63,6 +63,7 @@ import { SettingsPanel } from './SettingsPanel';
|
|
|
63
63
|
import { NumberPicker } from './NumberPicker';
|
|
64
64
|
import { ColorPicker } from './ColorPicker';
|
|
65
65
|
import { LayersPanel } from './LayersPanel';
|
|
66
|
+
import { applyPenDraft, cancelPenDraft, polylineDraftFromNv, syncPolylineDraftToNv, } from './penDraftUtils';
|
|
66
67
|
import { CloudMrNiivuePanel } from './CloudMrNiivuePanel';
|
|
67
68
|
import { Niivue } from './NiivuePatcher';
|
|
68
69
|
import NVSwitch from './Switch';
|
|
@@ -108,6 +109,7 @@ export var nv = new Niivue({
|
|
|
108
109
|
penType: NI_PEN_TYPE.PEN
|
|
109
110
|
});
|
|
110
111
|
nv.opts.penBounds = 0;
|
|
112
|
+
nv.opts.penSize = 1;
|
|
111
113
|
window.nv = nv;
|
|
112
114
|
// The NiiVue component wraps all other components in the UI.
|
|
113
115
|
// It is exported so that it can be used in other projects easily
|
|
@@ -171,15 +173,22 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
171
173
|
var _23 = useState(null), shapeDraft = _23[0], setShapeDraft = _23[1];
|
|
172
174
|
var _24 = useState("freehand"), penDrawMode = _24[0], setPenDrawMode = _24[1];
|
|
173
175
|
var _25 = useState(0), polylineVertexCount = _25[0], setPolylineVertexCount = _25[1];
|
|
176
|
+
var _26 = useState(null), penDraft = _26[0], setPenDraft = _26[1];
|
|
177
|
+
var _27 = useState(1), brushSize = _27[0], setBrushSize = _27[1];
|
|
174
178
|
var shapeDraftRef = React.useRef(null);
|
|
179
|
+
var penDraftRef = React.useRef(null);
|
|
175
180
|
var drawShapeToolRef = React.useRef(null);
|
|
181
|
+
var penDrawModeRef = React.useRef("freehand");
|
|
176
182
|
drawShapeToolRef.current = drawShapeTool;
|
|
177
183
|
shapeDraftRef.current = shapeDraft;
|
|
184
|
+
penDraftRef.current = penDraft;
|
|
185
|
+
penDrawModeRef.current = penDrawMode;
|
|
178
186
|
React.useEffect(function () {
|
|
179
187
|
nv.opts.penBounds = 0;
|
|
188
|
+
nv.opts.penSize = 1;
|
|
180
189
|
}, []);
|
|
181
|
-
var
|
|
182
|
-
var
|
|
190
|
+
var _28 = React.useState(1.0), gamma = _28[0], setGamma = _28[1];
|
|
191
|
+
var _29 = React.useState(0), gammaKey = _29[0], setGammaKey = _29[1];
|
|
183
192
|
// Niivue → React bridge so other places (Toolbar) can force the UI to reset
|
|
184
193
|
nv.onResetGamma = function () {
|
|
185
194
|
setGamma(1.0);
|
|
@@ -224,13 +233,13 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
224
233
|
// setLayers([...nv.volumes])
|
|
225
234
|
// }, [])
|
|
226
235
|
// values dualslider
|
|
227
|
-
var
|
|
236
|
+
var _30 = useState(0), rangeKey = _30[0], setRangeKey = _30[1];
|
|
228
237
|
nv.onResetContrast = function () {
|
|
229
238
|
setRangeKey(rangeKey + 1);
|
|
230
239
|
};
|
|
231
|
-
var
|
|
232
|
-
var
|
|
233
|
-
var
|
|
240
|
+
var _31 = useState([0, 0, 0]), boundMins = _31[0], setBoundMins = _31[1];
|
|
241
|
+
var _32 = useState([1, 1, 1]), boundMaxs = _32[0], setBoundMaxs = _32[1];
|
|
242
|
+
var _33 = useState([0.5, 0.5, 0.5]), mms = _33[0], setMMs = _33[1];
|
|
234
243
|
nv.onImageLoaded = function () {
|
|
235
244
|
var _a, _b;
|
|
236
245
|
var oldCrosshairPos = __spreadArray([], nv.scene.crosshairPos, true);
|
|
@@ -513,7 +522,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
513
522
|
setTextsVisible(true);
|
|
514
523
|
}
|
|
515
524
|
}
|
|
516
|
-
var
|
|
525
|
+
var _34 = useState("pan"), dragMode = _34[0], setDragMode = _34[1];
|
|
517
526
|
function nvSetDragMode(dragMode) {
|
|
518
527
|
switch (dragMode) {
|
|
519
528
|
case "none":
|
|
@@ -550,11 +559,15 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
550
559
|
nv.setDrawingEnabled(enabled);
|
|
551
560
|
nv.drawScene();
|
|
552
561
|
}
|
|
562
|
+
function nvUpdateBrushSize(size) {
|
|
563
|
+
setBrushSize(size);
|
|
564
|
+
nv.opts.penBounds = (size - 1) / 2;
|
|
565
|
+
nv.opts.penSize = size;
|
|
566
|
+
}
|
|
553
567
|
function nvUpdateDrawPen(a) {
|
|
554
568
|
var raw = Number(a.target.value);
|
|
555
569
|
setDrawPen(raw);
|
|
556
570
|
var penValue = raw;
|
|
557
|
-
nv.opts.penBounds = 0;
|
|
558
571
|
nv.setPenValue(penValue & 7, penValue > 0);
|
|
559
572
|
if (penValue == 8) {
|
|
560
573
|
nv.setPenValue(0, true);
|
|
@@ -722,7 +735,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
722
735
|
nv.opts.crosshairWidth = w;
|
|
723
736
|
nv.drawScene();
|
|
724
737
|
}
|
|
725
|
-
var
|
|
738
|
+
var _35 = useState({}), labelMapping = _35[0], setLabelMapping = _35[1];
|
|
726
739
|
function resampleImage(mapping) {
|
|
727
740
|
if (mapping === void 0) { mapping = labelMapping; }
|
|
728
741
|
var el = typeof document !== "undefined" ? document.getElementById("histoplot") : null;
|
|
@@ -738,30 +751,82 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
738
751
|
function syncPenDrawMode(mode) {
|
|
739
752
|
var _a;
|
|
740
753
|
setPenDrawMode(mode);
|
|
754
|
+
penDrawModeRef.current = mode;
|
|
741
755
|
nv.opts.polylinePenMode = mode === "polyline";
|
|
742
756
|
nv.opts.isFilledPen = mode === "freehand";
|
|
757
|
+
nv.opts.deferFreehandCommit =
|
|
758
|
+
drawShapeToolRef.current === "pen" && mode === "freehand";
|
|
743
759
|
if (mode === "freehand") {
|
|
744
760
|
(_a = nv.cloudMrCancelPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
745
761
|
}
|
|
746
762
|
}
|
|
747
|
-
function
|
|
763
|
+
function cancelPenDraftHandler() {
|
|
748
764
|
var _a;
|
|
749
|
-
|
|
765
|
+
var draft = penDraftRef.current;
|
|
766
|
+
if (!draft)
|
|
767
|
+
return;
|
|
768
|
+
cancelPenDraft(nv, draft);
|
|
769
|
+
if (draft.kind === "polyline") {
|
|
770
|
+
(_a = nv.cloudMrResetPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
771
|
+
setPolylineVertexCount(0);
|
|
772
|
+
}
|
|
773
|
+
setPenDraft(null);
|
|
774
|
+
penDraftRef.current = null;
|
|
775
|
+
nv._cloudMrPenDraftActive = false;
|
|
776
|
+
if (drawShapeToolRef.current === "pen") {
|
|
777
|
+
nvSetDrawingEnabled(true);
|
|
778
|
+
}
|
|
750
779
|
}
|
|
751
|
-
function
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
780
|
+
function applyPenDraftHandler(fillClosed) {
|
|
781
|
+
var _a;
|
|
782
|
+
if (fillClosed === void 0) { fillClosed = false; }
|
|
783
|
+
var draft = penDraftRef.current;
|
|
784
|
+
if (!draft)
|
|
785
|
+
return;
|
|
786
|
+
applyPenDraft(nv, draft, { fillClosed: fillClosed });
|
|
787
|
+
if (draft.kind === "polyline") {
|
|
788
|
+
(_a = nv.cloudMrResetPolyline) === null || _a === void 0 ? void 0 : _a.call(nv);
|
|
789
|
+
setPolylineVertexCount(0);
|
|
755
790
|
}
|
|
791
|
+
setPenDraft(null);
|
|
792
|
+
penDraftRef.current = null;
|
|
793
|
+
nv._cloudMrPenDraftActive = false;
|
|
794
|
+
setDrawingChanged(true);
|
|
795
|
+
if (drawShapeToolRef.current === "pen") {
|
|
796
|
+
nvSetDrawingEnabled(true);
|
|
797
|
+
}
|
|
798
|
+
resampleImage();
|
|
756
799
|
}
|
|
757
|
-
function
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
800
|
+
function onPenDraftChange(draft) {
|
|
801
|
+
setPenDraft(draft);
|
|
802
|
+
penDraftRef.current = draft;
|
|
803
|
+
if (draft.kind === "polyline") {
|
|
804
|
+
syncPolylineDraftToNv(nv, draft);
|
|
761
805
|
}
|
|
762
806
|
}
|
|
807
|
+
function cancelPolylineDraft() {
|
|
808
|
+
cancelPenDraftHandler();
|
|
809
|
+
}
|
|
810
|
+
nv.onPenDraftReady = function (draft) {
|
|
811
|
+
setPenDraft(draft);
|
|
812
|
+
penDraftRef.current = draft;
|
|
813
|
+
nv._cloudMrPenDraftActive = true;
|
|
814
|
+
nvSetDrawingEnabled(false);
|
|
815
|
+
};
|
|
763
816
|
nv.onPolylineChange = function (count) {
|
|
817
|
+
var _a;
|
|
764
818
|
setPolylineVertexCount(count);
|
|
819
|
+
if (penDrawModeRef.current === "polyline" && count >= 2) {
|
|
820
|
+
var draft = polylineDraftFromNv(nv);
|
|
821
|
+
if (draft) {
|
|
822
|
+
setPenDraft(draft);
|
|
823
|
+
penDraftRef.current = draft;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
else if (((_a = penDraftRef.current) === null || _a === void 0 ? void 0 : _a.kind) === "polyline") {
|
|
827
|
+
setPenDraft(null);
|
|
828
|
+
penDraftRef.current = null;
|
|
829
|
+
}
|
|
765
830
|
};
|
|
766
831
|
function cancelShapeDraft() {
|
|
767
832
|
var draft = shapeDraftRef.current;
|
|
@@ -801,7 +866,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
801
866
|
nvSetDrawingEnabled(false);
|
|
802
867
|
};
|
|
803
868
|
React.useEffect(function () {
|
|
804
|
-
if (!shapeDraft && !
|
|
869
|
+
if (!shapeDraft && !penDraft) {
|
|
805
870
|
return undefined;
|
|
806
871
|
}
|
|
807
872
|
var onKeyDown = function (event) {
|
|
@@ -810,8 +875,8 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
810
875
|
if (shapeDraft) {
|
|
811
876
|
cancelShapeDraft();
|
|
812
877
|
}
|
|
813
|
-
else if (
|
|
814
|
-
|
|
878
|
+
else if (penDraft) {
|
|
879
|
+
cancelPenDraftHandler();
|
|
815
880
|
}
|
|
816
881
|
return;
|
|
817
882
|
}
|
|
@@ -820,19 +885,19 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
820
885
|
if (shapeDraft) {
|
|
821
886
|
applyShapeDraft();
|
|
822
887
|
}
|
|
823
|
-
else if (
|
|
824
|
-
|
|
888
|
+
else if (penDraft) {
|
|
889
|
+
applyPenDraftHandler(false);
|
|
825
890
|
}
|
|
826
891
|
}
|
|
827
892
|
};
|
|
828
893
|
window.addEventListener("keydown", onKeyDown);
|
|
829
894
|
return function () { return window.removeEventListener("keydown", onKeyDown); };
|
|
830
|
-
}, [shapeDraft,
|
|
895
|
+
}, [shapeDraft, penDraft]);
|
|
831
896
|
function nvUpdateSelectionBoxColor(rgb01) {
|
|
832
897
|
setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
|
|
833
898
|
nv.setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
|
|
834
899
|
}
|
|
835
|
-
var
|
|
900
|
+
var _36 = React.useState('axial'), sliceType = _36[0], setSliceType = _36[1];
|
|
836
901
|
function nvUpdateSliceType(newSliceType) {
|
|
837
902
|
setSliceType(newSliceType);
|
|
838
903
|
if (newSliceType === 'axial') {
|
|
@@ -972,14 +1037,14 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
972
1037
|
return [2 /*return*/];
|
|
973
1038
|
});
|
|
974
1039
|
}); };
|
|
975
|
-
var
|
|
976
|
-
var
|
|
977
|
-
var
|
|
978
|
-
}), saveConfirmCallback =
|
|
979
|
-
var
|
|
980
|
-
var
|
|
981
|
-
var
|
|
982
|
-
var
|
|
1040
|
+
var _37 = useState(''), selectedROI = _37[0], setSelectedDrawingLayer = _37[1];
|
|
1041
|
+
var _38 = useState(false), saveDialogOpen = _38[0], setSaveDialogOpen = _38[1];
|
|
1042
|
+
var _39 = useState(function () {
|
|
1043
|
+
}), saveConfirmCallback = _39[0], setSaveConfirmCallback = _39[1];
|
|
1044
|
+
var _40 = useState(false), confirmationOpen = _40[0], setConfirmationOpen = _40[1];
|
|
1045
|
+
var _41 = useState(function () { }), warningConfirmationCallback = _41[0], setWarningConfirmationCallback = _41[1];
|
|
1046
|
+
var _42 = useState(function () { }), warningCancelCallback = _42[0], setWarningCancelCallback = _42[1];
|
|
1047
|
+
var _43 = useState(false), drawingChanged = _43[0], setDrawingChanged = _43[1];
|
|
983
1048
|
// When new drawing strokes are made on top of a saved ROI, reset the dropdown
|
|
984
1049
|
// so it shows the "ROI Layer" placeholder instead of the stale saved name.
|
|
985
1050
|
React.useEffect(function () {
|
|
@@ -1306,8 +1371,8 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1306
1371
|
cancelShapeDraft();
|
|
1307
1372
|
return;
|
|
1308
1373
|
}
|
|
1309
|
-
if (
|
|
1310
|
-
|
|
1374
|
+
if (penDraftRef.current) {
|
|
1375
|
+
cancelPenDraftHandler();
|
|
1311
1376
|
return;
|
|
1312
1377
|
}
|
|
1313
1378
|
nv.drawUndo();
|
|
@@ -1326,8 +1391,12 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1326
1391
|
onPenDrawModeChange: syncPenDrawMode,
|
|
1327
1392
|
polylineVertexCount: polylineVertexCount,
|
|
1328
1393
|
onCancelPolyline: cancelPolylineDraft,
|
|
1329
|
-
|
|
1330
|
-
|
|
1394
|
+
onApplyPenDraft: function () { return applyPenDraftHandler(false); },
|
|
1395
|
+
onCancelPenDraft: cancelPenDraftHandler,
|
|
1396
|
+
onCloseFillPenDraft: function () { return applyPenDraftHandler(true); },
|
|
1397
|
+
penDraftActive: penDraft != null,
|
|
1398
|
+
brushSize: brushSize,
|
|
1399
|
+
updateBrushSize: nvUpdateBrushSize
|
|
1331
1400
|
};
|
|
1332
1401
|
return (_jsxs(Box, __assign({ sx: {
|
|
1333
1402
|
display: 'flex',
|
|
@@ -1343,7 +1412,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1343
1412
|
props.rois[selectedROI].filename : undefined) }), props.niis[selectedVolume] != undefined && _jsx(CloudMrNiivuePanel, { nv: nv, transformFactors: transformFactors, decimalPrecision: decimalPrecision, locationData: locationData, locationTableVisible: locationTableVisible, pipelineID: props.pipelineID, resampleImage: resampleImage, rois: rois, drawToolkitProps: drawToolkitProps, drawShapeTool: drawShapeTool, setDrawShapeTool: setDrawShapeTool, mins: boundMins, maxs: boundMaxs, mms: mms, min: min, max: max, setMin: setMin, setMax: setMax, unzipAndRenderROI: unpackROI, zipAndSendROI: zipAndSendDrawingLayer, setLabelAlias: setLabelAlias, onAfterRoiUpload: function () {
|
|
1344
1413
|
var _a;
|
|
1345
1414
|
void ((_a = props.refreshPipelineRois) === null || _a === void 0 ? void 0 : _a.call(props));
|
|
1346
|
-
}, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: applyShapeDraft, onCancelShapeDraft: cancelShapeDraft }, "".concat(selectedVolume))] })));
|
|
1415
|
+
}, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: applyShapeDraft, onCancelShapeDraft: cancelShapeDraft, penDraft: penDraft, onPenDraftChange: onPenDraftChange, onApplyPenDraft: function () { return applyPenDraftHandler(false); }, onCancelPenDraft: cancelPenDraftHandler }, "".concat(selectedVolume))] })));
|
|
1347
1416
|
}
|
|
1348
1417
|
function niiToVolume(nii) {
|
|
1349
1418
|
return {
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "./shapeDraftUtils.js";
|
|
10
10
|
import {
|
|
11
11
|
addPolylineVertex,
|
|
12
|
+
axCorSagFromMouse,
|
|
12
13
|
cancelPolyline,
|
|
13
14
|
finishPolyline,
|
|
14
15
|
isClickWithoutDrag,
|
|
@@ -16,6 +17,10 @@ import {
|
|
|
16
17
|
previewPolylineSegment,
|
|
17
18
|
resetPolylineState,
|
|
18
19
|
} from "./polylinePenUtils.js";
|
|
20
|
+
import {
|
|
21
|
+
captureFreehandDraft,
|
|
22
|
+
shouldDeferFreehandCommit,
|
|
23
|
+
} from "./penDraftUtils.js";
|
|
19
24
|
|
|
20
25
|
/*
|
|
21
26
|
* bitmapOverlay — in-browser state only: remembers each voxel's label *before* Group
|
|
@@ -496,6 +501,31 @@ Niivue.prototype.drawPenFilled = function () {
|
|
|
496
501
|
this.drawBitmap[index] = value;
|
|
497
502
|
}
|
|
498
503
|
})
|
|
504
|
+
if (shouldDeferFreehandCommit(this) && this._cloudMrFreehandSessionStartBitmap) {
|
|
505
|
+
this._cloudMrSkipNextUndoBitmap = true;
|
|
506
|
+
this.hiddenBitmap = new Uint8Array(this.drawBitmap.length);
|
|
507
|
+
for (let i = 0; i < this.drawBitmap.length; i++) {
|
|
508
|
+
let pen = this.drawBitmap[i];
|
|
509
|
+
if (!this.getLabelVisibility(pen)) {
|
|
510
|
+
this.hiddenBitmap[i] = this.drawBitmap[i];
|
|
511
|
+
this.drawBitmap[i] = 0;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
this.refreshDrawing(false);
|
|
515
|
+
this.drawScene();
|
|
516
|
+
const axCorSag =
|
|
517
|
+
this.drawPenAxCorSag >= 0 ? this.drawPenAxCorSag : this._cloudMrFreehandAxCorSag;
|
|
518
|
+
const draft = captureFreehandDraft(
|
|
519
|
+
this,
|
|
520
|
+
this._cloudMrFreehandSessionStartBitmap,
|
|
521
|
+
axCorSag,
|
|
522
|
+
);
|
|
523
|
+
this._cloudMrFreehandSessionStartBitmap = null;
|
|
524
|
+
if (draft && typeof this.onPenDraftReady === "function") {
|
|
525
|
+
this.onPenDraftReady(draft);
|
|
526
|
+
}
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
499
529
|
this.drawAddUndoBitmap()
|
|
500
530
|
// Post-processing to hide hidden voxels
|
|
501
531
|
this.hiddenBitmap = new Uint8Array(this.drawBitmap.length);
|
|
@@ -1435,6 +1465,21 @@ Niivue.prototype.cloudMrResetPolyline = function cloudMrResetPolyline() {
|
|
|
1435
1465
|
resetPolylineState(this);
|
|
1436
1466
|
};
|
|
1437
1467
|
|
|
1468
|
+
const _mouseDownListener = Niivue.prototype.mouseDownListener;
|
|
1469
|
+
Niivue.prototype.mouseDownListener = function cloudMrMouseDownListener(e) {
|
|
1470
|
+
if (shouldDeferFreehandCommit(this) && this.drawBitmap) {
|
|
1471
|
+
this._cloudMrFreehandSessionStartBitmap = this.drawBitmap.slice();
|
|
1472
|
+
this._cloudMrFreehandAxCorSag = -1;
|
|
1473
|
+
}
|
|
1474
|
+
_mouseDownListener.call(this, e);
|
|
1475
|
+
if (shouldDeferFreehandCommit(this) && this._cloudMrFreehandSessionStartBitmap) {
|
|
1476
|
+
const axCorSag = axCorSagFromMouse(this);
|
|
1477
|
+
if (axCorSag >= 0) {
|
|
1478
|
+
this._cloudMrFreehandAxCorSag = axCorSag;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1438
1483
|
const _mouseClick = Niivue.prototype.mouseClick;
|
|
1439
1484
|
Niivue.prototype.mouseClick = function cloudMrMouseClick(...args) {
|
|
1440
1485
|
if (isPolylinePenActive(this)) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adjust handles for polyline (vertex drag) or freehand (bbox move/resize) drafts.
|
|
3
|
+
*/
|
|
4
|
+
export function PenDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
|
|
5
|
+
nv: any;
|
|
6
|
+
draft: any;
|
|
7
|
+
onDraftChange: any;
|
|
8
|
+
overlayKey: any;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
export default PenDraftOverlay;
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
13
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
14
|
+
if (ar || !(i in from)) {
|
|
15
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
16
|
+
ar[i] = from[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
|
+
};
|
|
21
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
23
|
+
import { boundsToFreehandCorners, redrawFreehandDraft, redrawPolylineDraft, resizeFreehandDraft, syncPolylineDraftToNv, translateFreehandDraft, translatePolylineVertices, updatePolylineVertex, } from "./penDraftUtils";
|
|
24
|
+
import { canvasDeltaToVoxDelta, clientToCanvasPos, translatePt, voxToOverlayPos, voxUnderClient, } from "./shapeDraftUtils";
|
|
25
|
+
var HANDLE_SIZE = 10;
|
|
26
|
+
var ACCENT = "#580f8b";
|
|
27
|
+
/**
|
|
28
|
+
* Adjust handles for polyline (vertex drag) or freehand (bbox move/resize) drafts.
|
|
29
|
+
*/
|
|
30
|
+
export function PenDraftOverlay(_a) {
|
|
31
|
+
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
|
|
32
|
+
var dragRef = useRef(null);
|
|
33
|
+
var draftRef = useRef(draft);
|
|
34
|
+
draftRef.current = draft;
|
|
35
|
+
var onPointerMoveRef = useRef(null);
|
|
36
|
+
var finishDragRef = useRef(null);
|
|
37
|
+
var _b = useState(0), setTick = _b[1];
|
|
38
|
+
var bump = useCallback(function () { return setTick(function (t) { return t + 1; }); }, []);
|
|
39
|
+
useEffect(function () {
|
|
40
|
+
var onMove = function () { return bump(); };
|
|
41
|
+
window.addEventListener("resize", onMove);
|
|
42
|
+
return function () { return window.removeEventListener("resize", onMove); };
|
|
43
|
+
}, [bump]);
|
|
44
|
+
var isPolyline = (draft === null || draft === void 0 ? void 0 : draft.kind) === "polyline";
|
|
45
|
+
var vertexCss = useMemo(function () {
|
|
46
|
+
if (!isPolyline || !(draft === null || draft === void 0 ? void 0 : draft.vertices))
|
|
47
|
+
return [];
|
|
48
|
+
return draft.vertices
|
|
49
|
+
.map(function (vox) { return voxToOverlayPos(nv, vox, draft.axCorSag); })
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
}, [draft, isPolyline, nv, overlayKey]);
|
|
52
|
+
var freehandCorners = useMemo(function () {
|
|
53
|
+
if (isPolyline || !(draft === null || draft === void 0 ? void 0 : draft.bounds))
|
|
54
|
+
return [];
|
|
55
|
+
return boundsToFreehandCorners(draft.bounds, draft.axCorSag);
|
|
56
|
+
}, [draft, isPolyline, overlayKey]);
|
|
57
|
+
var cornerCss = useMemo(function () {
|
|
58
|
+
if (isPolyline)
|
|
59
|
+
return vertexCss;
|
|
60
|
+
return freehandCorners
|
|
61
|
+
.map(function (vox) { return voxToOverlayPos(nv, vox, draft.axCorSag); })
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
}, [draft === null || draft === void 0 ? void 0 : draft.axCorSag, freehandCorners, isPolyline, nv, overlayKey, vertexCss]);
|
|
64
|
+
var centerCss = useMemo(function () {
|
|
65
|
+
var _a;
|
|
66
|
+
if (!draft)
|
|
67
|
+
return null;
|
|
68
|
+
if (isPolyline && ((_a = draft.vertices) === null || _a === void 0 ? void 0 : _a.length)) {
|
|
69
|
+
var xs = draft.vertices.map(function (v) { return v[0]; });
|
|
70
|
+
var ys = draft.vertices.map(function (v) { return v[1]; });
|
|
71
|
+
var zs = draft.vertices.map(function (v) { return v[2]; });
|
|
72
|
+
return voxToOverlayPos(nv, [
|
|
73
|
+
(Math.min.apply(Math, xs) + Math.max.apply(Math, xs)) / 2,
|
|
74
|
+
(Math.min.apply(Math, ys) + Math.max.apply(Math, ys)) / 2,
|
|
75
|
+
(Math.min.apply(Math, zs) + Math.max.apply(Math, zs)) / 2,
|
|
76
|
+
], draft.axCorSag);
|
|
77
|
+
}
|
|
78
|
+
if (draft.bounds) {
|
|
79
|
+
var b = draft.bounds;
|
|
80
|
+
return voxToOverlayPos(nv, [(b.x1 + b.x2) / 2, (b.y1 + b.y2) / 2, (b.z1 + b.z2) / 2], draft.axCorSag);
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}, [draft, isPolyline, nv, overlayKey]);
|
|
84
|
+
var boxStyle = useMemo(function () {
|
|
85
|
+
if (cornerCss.length < 2)
|
|
86
|
+
return null;
|
|
87
|
+
var xs = cornerCss.map(function (p) { return p.x; });
|
|
88
|
+
var ys = cornerCss.map(function (p) { return p.y; });
|
|
89
|
+
var left = Math.min.apply(Math, xs);
|
|
90
|
+
var top = Math.min.apply(Math, ys);
|
|
91
|
+
var width = Math.max.apply(Math, xs) - left;
|
|
92
|
+
var height = Math.max.apply(Math, ys) - top;
|
|
93
|
+
if (width < 2 && height < 2)
|
|
94
|
+
return null;
|
|
95
|
+
return { left: left, top: top, width: Math.max(width, 2), height: Math.max(height, 2) };
|
|
96
|
+
}, [cornerCss]);
|
|
97
|
+
var applyDraft = useCallback(function (nextDraft) {
|
|
98
|
+
if (nextDraft.kind === "polyline") {
|
|
99
|
+
redrawPolylineDraft(nv, nextDraft);
|
|
100
|
+
syncPolylineDraftToNv(nv, nextDraft);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
redrawFreehandDraft(nv, nextDraft);
|
|
104
|
+
}
|
|
105
|
+
onDraftChange(nextDraft);
|
|
106
|
+
}, [nv, onDraftChange]);
|
|
107
|
+
finishDragRef.current = function () {
|
|
108
|
+
dragRef.current = null;
|
|
109
|
+
if (onPointerMoveRef.current) {
|
|
110
|
+
window.removeEventListener("pointermove", onPointerMoveRef.current);
|
|
111
|
+
}
|
|
112
|
+
if (finishDragRef.current) {
|
|
113
|
+
window.removeEventListener("pointerup", finishDragRef.current);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
onPointerMoveRef.current = function (event) {
|
|
117
|
+
var drag = dragRef.current;
|
|
118
|
+
var current = draftRef.current;
|
|
119
|
+
if (!drag || !current)
|
|
120
|
+
return;
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
if (drag.mode === "move") {
|
|
123
|
+
var canvas = nv.canvas || document.getElementById("niiCanvas");
|
|
124
|
+
if (!canvas)
|
|
125
|
+
return;
|
|
126
|
+
var startCanvas = clientToCanvasPos(canvas, drag.startClientX, drag.startClientY);
|
|
127
|
+
var endCanvas = clientToCanvasPos(canvas, event.clientX, event.clientY);
|
|
128
|
+
var delta = canvasDeltaToVoxDelta(nv, startCanvas, endCanvas);
|
|
129
|
+
if (current.kind === "polyline") {
|
|
130
|
+
applyDraft(__assign(__assign({}, current), { vertices: translatePolylineVertices(drag.startVertices, delta) }));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
applyDraft(translateFreehandDraft(current, delta));
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
var vox = voxUnderClient(nv, event.clientX, event.clientY);
|
|
138
|
+
if (!vox)
|
|
139
|
+
return;
|
|
140
|
+
if (current.kind === "polyline") {
|
|
141
|
+
applyDraft(__assign(__assign({}, current), { vertices: updatePolylineVertex(drag.startVertices, drag.cornerIndex, vox) }));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
applyDraft(resizeFreehandDraft(current, drag.cornerIndex, vox));
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var startDrag = useCallback(function (event, mode, cornerIndex) {
|
|
148
|
+
var _a, _b;
|
|
149
|
+
if (cornerIndex === void 0) { cornerIndex = -1; }
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
event.stopPropagation();
|
|
152
|
+
dragRef.current = {
|
|
153
|
+
mode: mode,
|
|
154
|
+
cornerIndex: cornerIndex,
|
|
155
|
+
startClientX: event.clientX,
|
|
156
|
+
startClientY: event.clientY,
|
|
157
|
+
startVertices: (_b = (_a = draft.vertices) === null || _a === void 0 ? void 0 : _a.map(function (v) { return __spreadArray([], v, true); })) !== null && _b !== void 0 ? _b : []
|
|
158
|
+
};
|
|
159
|
+
window.addEventListener("pointermove", onPointerMoveRef.current);
|
|
160
|
+
window.addEventListener("pointerup", finishDragRef.current);
|
|
161
|
+
}, [draft.vertices]);
|
|
162
|
+
useEffect(function () { return function () {
|
|
163
|
+
var _a;
|
|
164
|
+
(_a = finishDragRef.current) === null || _a === void 0 ? void 0 : _a.call(finishDragRef);
|
|
165
|
+
}; }, []);
|
|
166
|
+
if (!draft || !boxStyle)
|
|
167
|
+
return null;
|
|
168
|
+
var handleStyle = {
|
|
169
|
+
position: "absolute",
|
|
170
|
+
width: HANDLE_SIZE,
|
|
171
|
+
height: HANDLE_SIZE,
|
|
172
|
+
marginLeft: -HANDLE_SIZE / 2,
|
|
173
|
+
marginTop: -HANDLE_SIZE / 2,
|
|
174
|
+
borderRadius: "50%",
|
|
175
|
+
background: "#fff",
|
|
176
|
+
border: "2px solid ".concat(ACCENT),
|
|
177
|
+
boxSizing: "border-box",
|
|
178
|
+
cursor: "pointer",
|
|
179
|
+
zIndex: 3,
|
|
180
|
+
touchAction: "none",
|
|
181
|
+
pointerEvents: "auto"
|
|
182
|
+
};
|
|
183
|
+
return (_jsxs("div", __assign({ style: {
|
|
184
|
+
position: "absolute",
|
|
185
|
+
inset: 0,
|
|
186
|
+
zIndex: 2,
|
|
187
|
+
pointerEvents: "none",
|
|
188
|
+
overflow: "hidden"
|
|
189
|
+
}, "aria-label": "Adjust pen ROI" }, { children: [_jsx("div", { style: {
|
|
190
|
+
position: "absolute",
|
|
191
|
+
left: boxStyle.left,
|
|
192
|
+
top: boxStyle.top,
|
|
193
|
+
width: boxStyle.width,
|
|
194
|
+
height: boxStyle.height,
|
|
195
|
+
border: "2px dashed ".concat(ACCENT),
|
|
196
|
+
background: "rgba(88, 15, 139, 0.08)",
|
|
197
|
+
boxSizing: "border-box",
|
|
198
|
+
pointerEvents: "none"
|
|
199
|
+
} }), centerCss && (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "move"); }, style: __assign(__assign({}, handleStyle), { left: centerCss.x, top: centerCss.y, cursor: "move", borderRadius: 2, width: 12, height: 12, marginLeft: -6, marginTop: -6 }), title: "Move shape" })), cornerCss.map(function (pos, i) { return (_jsx("div", { role: "presentation", onPointerDown: function (e) { return startDrag(e, "corner", i); }, style: __assign(__assign({}, handleStyle), { left: pos.x, top: pos.y, cursor: isPolyline ? "grab" : "nwse-resize" }), title: isPolyline ? "Move vertex" : "Resize shape" }, "handle-".concat(i))); })] })));
|
|
200
|
+
}
|
|
201
|
+
export default PenDraftOverlay;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { Stack, Slider, Typography } from "@mui/material";
|
|
14
|
+
var SLIDER_SX = {
|
|
15
|
+
width: "80%",
|
|
16
|
+
color: "#fff",
|
|
17
|
+
"& .MuiSlider-track": { backgroundColor: "#fff", border: "none" },
|
|
18
|
+
"& .MuiSlider-rail": { backgroundColor: "rgba(255,255,255,0.3)" },
|
|
19
|
+
"& .MuiSlider-thumb": {
|
|
20
|
+
backgroundColor: "#fff",
|
|
21
|
+
border: "2px solid #fff",
|
|
22
|
+
"&:hover, &.Mui-focusVisible, &.Mui-active": {
|
|
23
|
+
boxShadow: "0 0 0 8px rgba(255,255,255,0.16)"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"& .MuiSlider-mark": { backgroundColor: "#fff" },
|
|
27
|
+
"& .MuiSlider-markActive": { backgroundColor: "#fff" },
|
|
28
|
+
"& .MuiSlider-valueLabel": {
|
|
29
|
+
backgroundColor: "#fff",
|
|
30
|
+
color: "#000"
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export function BrushSizeSlider(_a) {
|
|
34
|
+
var label = _a.label, brushSize = _a.brushSize, updateBrushSize = _a.updateBrushSize;
|
|
35
|
+
return (_jsxs(Stack, __assign({ sx: { mb: 0.5, px: 0.5 }, alignItems: "center", width: "100%" }, { children: [_jsxs(Typography, __assign({ color: "white", noWrap: true, width: "100%", marginLeft: "6pt", fontSize: "11pt", sx: { userSelect: "none" } }, { children: [label, ": ", brushSize] })), _jsx(Slider, { value: brushSize, sx: SLIDER_SX, step: 2, min: 1, max: 15, marks: true, onChange: function (_event, value) { return updateBrushSize(value); } })] })));
|
|
36
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount,
|
|
1
|
+
export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEnabled, showPenModes, penDrawMode, onPenDrawModeChange, polylineVertexCount, penDraftActive, onApplyPenDraft, onCancelPenDraft, onCloseFillPenDraft, brushSize, updateBrushSize, shapeDraftActive, onApplyShapeDraft, onCancelShapeDraft, }: {
|
|
2
2
|
expanded: any;
|
|
3
3
|
updateDrawPen: any;
|
|
4
4
|
setDrawingEnabled: any;
|
|
@@ -6,9 +6,12 @@ export default function DrawColorPlatte({ expanded, updateDrawPen, setDrawingEna
|
|
|
6
6
|
penDrawMode?: string | undefined;
|
|
7
7
|
onPenDrawModeChange: any;
|
|
8
8
|
polylineVertexCount?: number | undefined;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
penDraftActive?: boolean | undefined;
|
|
10
|
+
onApplyPenDraft: any;
|
|
11
|
+
onCancelPenDraft: any;
|
|
12
|
+
onCloseFillPenDraft: any;
|
|
13
|
+
brushSize?: number | undefined;
|
|
14
|
+
updateBrushSize: any;
|
|
12
15
|
shapeDraftActive?: boolean | undefined;
|
|
13
16
|
onApplyShapeDraft: any;
|
|
14
17
|
onCancelShapeDraft: any;
|
|
@@ -11,13 +11,13 @@ var __assign = (this && this.__assign) || function () {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
* Pen palette adds freehand vs straight-line mode; rectangle/ellipse show Apply/Cancel while adjusting.
|
|
14
|
+
* Pen palette adds freehand vs polyline mode; pen/shape drafts show Apply/Cancel while adjusting.
|
|
16
15
|
*/
|
|
17
16
|
import { Stack, IconButton, Button, Tooltip, Typography } from "@mui/material";
|
|
18
17
|
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
|
19
18
|
import CheckIcon from "@mui/icons-material/Check";
|
|
20
19
|
import CloseIcon from "@mui/icons-material/Close";
|
|
20
|
+
import { BrushSizeSlider } from "./BrushSizeSlider";
|
|
21
21
|
var FILLED_COLORS = [
|
|
22
22
|
{ sx: { color: "red" } },
|
|
23
23
|
{ sx: { color: "green" } },
|
|
@@ -37,7 +37,7 @@ var modeBtnSx = function (active) { return ({
|
|
|
37
37
|
px: 0.75
|
|
38
38
|
}); };
|
|
39
39
|
export default function DrawColorPlatte(_a) {
|
|
40
|
-
var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d,
|
|
40
|
+
var expanded = _a.expanded, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.showPenModes, showPenModes = _b === void 0 ? false : _b, _c = _a.penDrawMode, penDrawMode = _c === void 0 ? "freehand" : _c, onPenDrawModeChange = _a.onPenDrawModeChange, _d = _a.polylineVertexCount, polylineVertexCount = _d === void 0 ? 0 : _d, _e = _a.penDraftActive, penDraftActive = _e === void 0 ? false : _e, onApplyPenDraft = _a.onApplyPenDraft, onCancelPenDraft = _a.onCancelPenDraft, onCloseFillPenDraft = _a.onCloseFillPenDraft, _f = _a.brushSize, brushSize = _f === void 0 ? 1 : _f, updateBrushSize = _a.updateBrushSize, _g = _a.shapeDraftActive, shapeDraftActive = _g === void 0 ? false : _g, onApplyShapeDraft = _a.onApplyShapeDraft, onCancelShapeDraft = _a.onCancelShapeDraft;
|
|
41
41
|
return (_jsxs(Stack, __assign({ style: {
|
|
42
42
|
position: "absolute",
|
|
43
43
|
top: "100%",
|
|
@@ -51,10 +51,10 @@ export default function DrawColorPlatte(_a) {
|
|
|
51
51
|
borderTopLeftRadius: "6pt",
|
|
52
52
|
borderTopRightRadius: "6pt",
|
|
53
53
|
background: "#333"
|
|
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: "
|
|
54
|
+
}, direction: "column", spacing: 0.5, sx: { py: expanded ? 0.5 : 0 } }, { children: [showPenModes && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 0.5, sx: { px: 0.75, pt: 0.25 } }, { children: [_jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("freehand"); }, sx: modeBtnSx(penDrawMode === "freehand") }, { children: "Freehand" })), _jsx(Button, __assign({ size: "small", onClick: function () { return onPenDrawModeChange === null || onPenDrawModeChange === void 0 ? void 0 : onPenDrawModeChange("polyline"); }, sx: modeBtnSx(penDrawMode === "polyline") }, { children: "Polyline" }))] }))), showPenModes && expanded && updateBrushSize && (_jsx(BrushSizeSlider, { label: "Line thickness", brushSize: brushSize, updateBrushSize: updateBrushSize })), _jsx(Stack, __assign({ direction: "row" }, { children: FILLED_COLORS.map(function (color, index) { return (_jsx(IconButton, __assign({ onClick: function () {
|
|
55
55
|
updateDrawPen({ target: { value: index + 1 } });
|
|
56
56
|
setDrawingEnabled(true);
|
|
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
|
|
57
|
+
} }, { children: _jsx(FiberManualRecordIcon, { sx: color.sx }) }), index)); }) })), showPenModes && penDrawMode === "polyline" && expanded && polylineVertexCount === 0 && (_jsx(Typography, __assign({ sx: { px: 1, pb: 0.5, fontSize: "0.68rem", color: "#aaa", userSelect: "none" } }, { children: "Click each vertex to draw connected line segments" }))), showPenModes && penDraftActive && expanded && (_jsxs(Stack, __assign({ direction: "row", alignItems: "center", justifyContent: "space-between", sx: { px: 1, py: 0.5, borderTop: "1px solid #555", width: "100%" } }, { children: [_jsx(Tooltip, __assign({ title: "Cancel (Esc)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "cancel pen draft", onClick: function () { return onCancelPenDraft === null || onCancelPenDraft === void 0 ? void 0 : onCancelPenDraft(); }, startIcon: _jsx(CloseIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
|
|
58
58
|
color: "#ccc",
|
|
59
59
|
fontSize: ACTION_FONT_SIZE,
|
|
60
60
|
textTransform: "none",
|
|
@@ -62,14 +62,14 @@ export default function DrawColorPlatte(_a) {
|
|
|
62
62
|
py: 0.25,
|
|
63
63
|
px: 0.75,
|
|
64
64
|
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
65
|
-
} }, { children: "Cancel" })) })), _jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 1 }, { children: [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
|
|
65
|
+
} }, { children: "Cancel" })) })), _jsxs(Stack, __assign({ direction: "row", alignItems: "center", spacing: 1 }, { children: [penDrawMode === "polyline" && polylineVertexCount >= 3 && (_jsx(Tooltip, __assign({ title: "Connect last point to first and fill" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "close and fill polyline", onClick: function () { return onCloseFillPenDraft === null || onCloseFillPenDraft === void 0 ? void 0 : onCloseFillPenDraft(); }, sx: {
|
|
66
66
|
color: "#c9a0e8",
|
|
67
67
|
fontSize: ACTION_FONT_SIZE,
|
|
68
68
|
textTransform: "none",
|
|
69
69
|
minWidth: 0,
|
|
70
70
|
py: 0.25,
|
|
71
71
|
px: 0.75
|
|
72
|
-
} }, { children: "Close & fill" })) }))), _jsx(Tooltip, __assign({ title: "
|
|
72
|
+
} }, { children: "Close & fill" })) }))), _jsx(Tooltip, __assign({ title: "Apply (Enter)" }, { children: _jsx(Button, __assign({ size: "small", "aria-label": "apply pen draft", onClick: function () { return onApplyPenDraft === null || onApplyPenDraft === void 0 ? void 0 : onApplyPenDraft(); }, startIcon: _jsx(CheckIcon, { sx: { fontSize: ACTION_ICON_SIZE } }), sx: {
|
|
73
73
|
color: "#c9a0e8",
|
|
74
74
|
fontSize: ACTION_FONT_SIZE,
|
|
75
75
|
textTransform: "none",
|
|
@@ -77,7 +77,7 @@ export default function DrawColorPlatte(_a) {
|
|
|
77
77
|
py: 0.25,
|
|
78
78
|
px: 0.75,
|
|
79
79
|
"& .MuiButton-startIcon": { mr: 0.5, ml: 0 }
|
|
80
|
-
} }, { children: "
|
|
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
81
|
color: "#ccc",
|
|
82
82
|
fontSize: ACTION_FONT_SIZE,
|
|
83
83
|
textTransform: "none",
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
export default EraserPlatte
|
|
2
|
-
|
|
1
|
+
export default function EraserPlatte({ expandEraseOptions, updateDrawPen, setDrawingEnabled, brushSize, updateBrushSize, }: {
|
|
2
|
+
expandEraseOptions: any;
|
|
3
|
+
updateDrawPen: any;
|
|
4
|
+
setDrawingEnabled: any;
|
|
5
|
+
brushSize?: number | undefined;
|
|
6
|
+
updateBrushSize: any;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,29 +1,26 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
1
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
13
|
import { Stack, IconButton } from "@mui/material";
|
|
3
14
|
import FiberManualRecordIcon from "@mui/icons-material/FiberManualRecord";
|
|
4
15
|
import FiberManualRecordOutlinedIcon from "@mui/icons-material/FiberManualRecordOutlined";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
function (t) {
|
|
9
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
10
|
-
s = arguments[i];
|
|
11
|
-
for (var p in s)
|
|
12
|
-
if (Object.prototype.hasOwnProperty.call(s, p))
|
|
13
|
-
t[p] = s[p];
|
|
14
|
-
}
|
|
15
|
-
return t;
|
|
16
|
-
};
|
|
17
|
-
return __assign.apply(this, arguments);
|
|
18
|
-
};
|
|
19
|
-
var EraserPlatte = function (_a) {
|
|
20
|
-
var expandEraseOptions = _a.expandEraseOptions, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled;
|
|
16
|
+
import { BrushSizeSlider } from "./BrushSizeSlider";
|
|
17
|
+
export default function EraserPlatte(_a) {
|
|
18
|
+
var expandEraseOptions = _a.expandEraseOptions, updateDrawPen = _a.updateDrawPen, setDrawingEnabled = _a.setDrawingEnabled, _b = _a.brushSize, brushSize = _b === void 0 ? 1 : _b, updateBrushSize = _a.updateBrushSize;
|
|
21
19
|
var eraseOptions = [
|
|
22
20
|
_jsx(FiberManualRecordIcon, { style: { color: "white" } }, "e0"),
|
|
23
21
|
_jsx(FiberManualRecordOutlinedIcon, { style: { color: "white" } }, "e1"),
|
|
24
22
|
];
|
|
25
|
-
return _jsxs(Stack, __assign({
|
|
26
|
-
style: {
|
|
23
|
+
return (_jsxs(Stack, __assign({ style: {
|
|
27
24
|
position: "absolute",
|
|
28
25
|
top: "100%",
|
|
29
26
|
left: 0,
|
|
@@ -36,21 +33,8 @@ var EraserPlatte = function (_a) {
|
|
|
36
33
|
borderTopRightRadius: "6pt",
|
|
37
34
|
background: "#333",
|
|
38
35
|
width: 150
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
children: eraseOptions.map(function (value, index) {
|
|
45
|
-
return _jsx(IconButton, __assign({
|
|
46
|
-
onClick: function () {
|
|
47
|
-
updateDrawPen({ target: { value: index === 0 ? 8 : 0 } });
|
|
48
|
-
setDrawingEnabled(true);
|
|
49
|
-
}
|
|
50
|
-
}, { children: value }), index);
|
|
51
|
-
})
|
|
52
|
-
})),
|
|
53
|
-
]
|
|
54
|
-
}));
|
|
55
|
-
};
|
|
56
|
-
export default EraserPlatte;
|
|
36
|
+
}, direction: "column" }, { children: [updateBrushSize && (_jsx(BrushSizeSlider, { label: "Eraser size", brushSize: brushSize, updateBrushSize: updateBrushSize })), _jsx(Stack, __assign({ direction: "row", style: { justifyContent: "center" } }, { children: eraseOptions.map(function (value, index) { return (_jsx(IconButton, __assign({ onClick: function () {
|
|
37
|
+
updateDrawPen({ target: { value: index === 0 ? 8 : 0 } });
|
|
38
|
+
setDrawingEnabled(true);
|
|
39
|
+
} }, { children: value }), index)); }) }))] })));
|
|
40
|
+
}
|
|
@@ -71,13 +71,15 @@ export function MroDrawToolkit(props) {
|
|
|
71
71
|
var _d = useState(undefined), setMaskColor = _d[1];
|
|
72
72
|
var filled = props.drawPen > 7;
|
|
73
73
|
useEffect(function () {
|
|
74
|
-
if (!props.shapeDraftActive)
|
|
74
|
+
if (!props.shapeDraftActive && !props.penDraftActive)
|
|
75
75
|
return;
|
|
76
76
|
if (drawShapeTool === "rectangle")
|
|
77
77
|
setExpandedOption("r");
|
|
78
78
|
else if (drawShapeTool === "ellipse")
|
|
79
79
|
setExpandedOption("l");
|
|
80
|
-
|
|
80
|
+
else if (drawShapeTool === "pen")
|
|
81
|
+
setExpandedOption("d");
|
|
82
|
+
}, [props.shapeDraftActive, props.penDraftActive, drawShapeTool]);
|
|
81
83
|
var shapeSelectedSx = function (shape) {
|
|
82
84
|
return drawShapeTool === shape
|
|
83
85
|
? { backgroundColor: "rgba(88, 15, 139, 0.12)", color: "#580f8b" }
|
|
@@ -149,7 +151,7 @@ export function MroDrawToolkit(props) {
|
|
|
149
151
|
var _a;
|
|
150
152
|
if (clickTargetIsNiivueCanvas(event.target))
|
|
151
153
|
return;
|
|
152
|
-
if (props.shapeDraftActive)
|
|
154
|
+
if (props.shapeDraftActive || props.penDraftActive)
|
|
153
155
|
return;
|
|
154
156
|
setExpandedOption("n");
|
|
155
157
|
setExpandOpacityOptions(false);
|
|
@@ -170,7 +172,7 @@ export function MroDrawToolkit(props) {
|
|
|
170
172
|
alignItems: "center",
|
|
171
173
|
gap: 4,
|
|
172
174
|
overflow: "visible"
|
|
173
|
-
} }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, showPenModes: true, penDrawMode: props.penDrawMode, onPenDrawModeChange: props.onPenDrawModeChange, polylineVertexCount: props.polylineVertexCount, onCancelPolyline: props.onCancelPolyline,
|
|
175
|
+
} }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, showPenModes: true, penDrawMode: props.penDrawMode, onPenDrawModeChange: props.onPenDrawModeChange, polylineVertexCount: props.polylineVertexCount, onCancelPolyline: props.onCancelPolyline, penDraftActive: props.penDraftActive, onApplyPenDraft: props.onApplyPenDraft, onCancelPenDraft: props.onCancelPenDraft, onCloseFillPenDraft: props.onCloseFillPenDraft, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "r" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "rectangle", onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "l" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Ellipse" }, { children: _jsx(IconButton, __assign({ "aria-label": "ellipse", size: "small", onClick: clickEllipse, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("ellipse")) }, { children: _jsx(CircleOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "l", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, shapeDraftActive: props.shapeDraftActive && drawShapeTool === "ellipse", onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "e" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: toolBtnSx }, { children: filled || expandedOption !== "e" ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled, brushSize: props.brushSize, updateBrushSize: props.updateBrushSize })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "Save screenshot" }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ "aria-label": "capture", size: "small", disabled: !vol, onClick: function () { return vol && props.nv.saveScene("".concat(vol.name, "_drawing.png")); }, sx: toolBtnSx }, { children: _jsx(CameraAltIcon, { sx: { color: ICON_COLOR } }) })) }) })), _jsx(Tooltip, __assign({ title: "Clear drawing" }, { children: _jsx(IconButton, __assign({ "aria-label": "delete", size: "small", onClick: function () {
|
|
174
176
|
props.nv.clearDrawing();
|
|
175
177
|
props.resampleImage();
|
|
176
178
|
props.setDrawingChanged(false);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/** @typedef {'polyline' | 'freehand'} PenDraftKind */
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {Object} PenDraft
|
|
4
|
+
* @property {PenDraftKind} kind
|
|
5
|
+
* @property {Uint8Array} baseBitmap
|
|
6
|
+
* @property {number} axCorSag
|
|
7
|
+
* @property {number} penValue
|
|
8
|
+
* @property {[number, number, number][]} [vertices]
|
|
9
|
+
* @property {[number, number, number][]} [strokeVoxels]
|
|
10
|
+
* @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
|
|
11
|
+
*/
|
|
12
|
+
export function isFreehandPenActive(nv: any): any;
|
|
13
|
+
export function shouldDeferFreehandCommit(nv: any): any;
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {Object} PenDraft
|
|
16
|
+
* @property {PenDraftKind} kind
|
|
17
|
+
* @property {Uint8Array} baseBitmap
|
|
18
|
+
* @property {number} axCorSag
|
|
19
|
+
* @property {number} penValue
|
|
20
|
+
* @property {[number, number, number][]} [vertices]
|
|
21
|
+
* @property {[number, number, number][]} [strokeVoxels]
|
|
22
|
+
* @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
|
|
23
|
+
*/
|
|
24
|
+
export function redrawPolylineDraft(nv: any, draft: any): void;
|
|
25
|
+
export function translatePolylineVertices(vertices: any, delta: any): any;
|
|
26
|
+
export function updatePolylineVertex(vertices: any, index: any, newVox: any): any;
|
|
27
|
+
export function syncPolylineDraftToNv(nv: any, draft: any): void;
|
|
28
|
+
export function captureFreehandDraft(nv: any, sessionStartBitmap: any, axCorSag: any): {
|
|
29
|
+
kind: string;
|
|
30
|
+
baseBitmap: Uint8Array;
|
|
31
|
+
axCorSag: any;
|
|
32
|
+
penValue: any;
|
|
33
|
+
strokeVoxels: number[][];
|
|
34
|
+
bounds: {
|
|
35
|
+
x1: number;
|
|
36
|
+
y1: number;
|
|
37
|
+
z1: number;
|
|
38
|
+
x2: number;
|
|
39
|
+
y2: number;
|
|
40
|
+
z2: number;
|
|
41
|
+
};
|
|
42
|
+
} | null;
|
|
43
|
+
export function redrawFreehandDraft(nv: any, draft: any): void;
|
|
44
|
+
export function translateFreehandDraft(draft: any, delta: any): any;
|
|
45
|
+
export function resizeFreehandDraft(draft: any, cornerIndex: any, newVox: any): any;
|
|
46
|
+
export function boundsToFreehandCorners(bounds: any, axCorSag: any): any[][];
|
|
47
|
+
export function polylineDraftFromNv(nv: any): {
|
|
48
|
+
kind: string;
|
|
49
|
+
vertices: any;
|
|
50
|
+
baseBitmap: Uint8Array;
|
|
51
|
+
axCorSag: any;
|
|
52
|
+
penValue: any;
|
|
53
|
+
} | null;
|
|
54
|
+
export function applyPenDraft(nv: any, draft: any, { fillClosed }?: {
|
|
55
|
+
fillClosed?: boolean | undefined;
|
|
56
|
+
}): void;
|
|
57
|
+
export function cancelPenDraft(nv: any, draft: any): void;
|
|
58
|
+
export type PenDraftKind = 'polyline' | 'freehand';
|
|
59
|
+
export type PenDraft = {
|
|
60
|
+
kind: PenDraftKind;
|
|
61
|
+
baseBitmap: Uint8Array;
|
|
62
|
+
axCorSag: number;
|
|
63
|
+
penValue: number;
|
|
64
|
+
vertices?: [number, number, number][] | undefined;
|
|
65
|
+
strokeVoxels?: [number, number, number][] | undefined;
|
|
66
|
+
bounds?: {
|
|
67
|
+
x1: number;
|
|
68
|
+
y1: number;
|
|
69
|
+
x2: number;
|
|
70
|
+
y2: number;
|
|
71
|
+
z1: number;
|
|
72
|
+
z2: number;
|
|
73
|
+
} | undefined;
|
|
74
|
+
};
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
13
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
14
|
+
if (ar || !(i in from)) {
|
|
15
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
16
|
+
ar[i] = from[i];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
20
|
+
};
|
|
21
|
+
import { translatePt } from "./shapeDraftUtils";
|
|
22
|
+
import { NI_PEN_TYPE } from "./niivuePenType";
|
|
23
|
+
/** @typedef {'polyline' | 'freehand'} PenDraftKind */
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {Object} PenDraft
|
|
26
|
+
* @property {PenDraftKind} kind
|
|
27
|
+
* @property {Uint8Array} baseBitmap
|
|
28
|
+
* @property {number} axCorSag
|
|
29
|
+
* @property {number} penValue
|
|
30
|
+
* @property {[number, number, number][]} [vertices]
|
|
31
|
+
* @property {[number, number, number][]} [strokeVoxels]
|
|
32
|
+
* @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
|
|
33
|
+
*/
|
|
34
|
+
export function isFreehandPenActive(nv) {
|
|
35
|
+
return (nv.opts.isFilledPen &&
|
|
36
|
+
nv.opts.drawingEnabled &&
|
|
37
|
+
nv.opts.penType === NI_PEN_TYPE.PEN &&
|
|
38
|
+
!nv.opts.polylinePenMode);
|
|
39
|
+
}
|
|
40
|
+
export function shouldDeferFreehandCommit(nv) {
|
|
41
|
+
return !!nv.opts.deferFreehandCommit && isFreehandPenActive(nv);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} PenDraft
|
|
45
|
+
* @property {PenDraftKind} kind
|
|
46
|
+
* @property {Uint8Array} baseBitmap
|
|
47
|
+
* @property {number} axCorSag
|
|
48
|
+
* @property {number} penValue
|
|
49
|
+
* @property {[number, number, number][]} [vertices]
|
|
50
|
+
* @property {[number, number, number][]} [strokeVoxels]
|
|
51
|
+
* @property {{ x1: number, y1: number, x2: number, y2: number, z1: number, z2: number }} [bounds]
|
|
52
|
+
*/
|
|
53
|
+
export function redrawPolylineDraft(nv, draft) {
|
|
54
|
+
var _a;
|
|
55
|
+
if (!((_a = draft === null || draft === void 0 ? void 0 : draft.vertices) === null || _a === void 0 ? void 0 : _a.length) || !draft.baseBitmap)
|
|
56
|
+
return;
|
|
57
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
58
|
+
nv.drawPenAxCorSag = draft.axCorSag;
|
|
59
|
+
for (var i = 1; i < draft.vertices.length; i++) {
|
|
60
|
+
nv.drawPenLine(draft.vertices[i], draft.vertices[i - 1], draft.penValue);
|
|
61
|
+
}
|
|
62
|
+
nv.refreshDrawing(false, false);
|
|
63
|
+
nv.drawScene();
|
|
64
|
+
}
|
|
65
|
+
export function translatePolylineVertices(vertices, delta) {
|
|
66
|
+
return vertices.map(function (v) { return translatePt(v, delta); });
|
|
67
|
+
}
|
|
68
|
+
export function updatePolylineVertex(vertices, index, newVox) {
|
|
69
|
+
var next = vertices.map(function (v) { return __spreadArray([], v, true); });
|
|
70
|
+
next[index] = __spreadArray([], newVox, true);
|
|
71
|
+
return next;
|
|
72
|
+
}
|
|
73
|
+
export function syncPolylineDraftToNv(nv, draft) {
|
|
74
|
+
nv._cloudMrPolylineVertices = draft.vertices.map(function (v) { return __spreadArray([], v, true); });
|
|
75
|
+
if (nv.drawBitmap) {
|
|
76
|
+
nv._cloudMrPolylineBaseBitmap = nv.drawBitmap.slice();
|
|
77
|
+
}
|
|
78
|
+
nv._cloudMrPolylineAxCorSag = draft.axCorSag;
|
|
79
|
+
}
|
|
80
|
+
export function captureFreehandDraft(nv, sessionStartBitmap, axCorSag) {
|
|
81
|
+
var _a;
|
|
82
|
+
if (!nv.drawBitmap || !sessionStartBitmap)
|
|
83
|
+
return null;
|
|
84
|
+
var penValue = nv.opts.penValue;
|
|
85
|
+
var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
|
|
86
|
+
if (!dims)
|
|
87
|
+
return null;
|
|
88
|
+
var strokeVoxels = [];
|
|
89
|
+
for (var i = 0; i < nv.drawBitmap.length; i++) {
|
|
90
|
+
if (nv.drawBitmap[i] === penValue && sessionStartBitmap[i] !== penValue) {
|
|
91
|
+
var z = Math.floor(i / (dims[1] * dims[2]));
|
|
92
|
+
var rem = i - z * dims[1] * dims[2];
|
|
93
|
+
var y = Math.floor(rem / dims[1]);
|
|
94
|
+
var x = rem % dims[1];
|
|
95
|
+
strokeVoxels.push([x, y, z]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (strokeVoxels.length === 0)
|
|
99
|
+
return null;
|
|
100
|
+
var x1 = Infinity;
|
|
101
|
+
var y1 = Infinity;
|
|
102
|
+
var z1 = Infinity;
|
|
103
|
+
var x2 = -Infinity;
|
|
104
|
+
var y2 = -Infinity;
|
|
105
|
+
var z2 = -Infinity;
|
|
106
|
+
for (var _i = 0, strokeVoxels_1 = strokeVoxels; _i < strokeVoxels_1.length; _i++) {
|
|
107
|
+
var _b = strokeVoxels_1[_i], x = _b[0], y = _b[1], z = _b[2];
|
|
108
|
+
x1 = Math.min(x1, x);
|
|
109
|
+
y1 = Math.min(y1, y);
|
|
110
|
+
z1 = Math.min(z1, z);
|
|
111
|
+
x2 = Math.max(x2, x);
|
|
112
|
+
y2 = Math.max(y2, y);
|
|
113
|
+
z2 = Math.max(z2, z);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
kind: "freehand",
|
|
117
|
+
baseBitmap: new Uint8Array(sessionStartBitmap),
|
|
118
|
+
axCorSag: axCorSag,
|
|
119
|
+
penValue: penValue,
|
|
120
|
+
strokeVoxels: strokeVoxels,
|
|
121
|
+
bounds: { x1: x1, y1: y1, z1: z1, x2: x2, y2: y2, z2: z2 }
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function redrawFreehandDraft(nv, draft) {
|
|
125
|
+
var _a;
|
|
126
|
+
if (!((_a = draft === null || draft === void 0 ? void 0 : draft.strokeVoxels) === null || _a === void 0 ? void 0 : _a.length) || !draft.baseBitmap)
|
|
127
|
+
return;
|
|
128
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
129
|
+
for (var _i = 0, _b = draft.strokeVoxels; _i < _b.length; _i++) {
|
|
130
|
+
var _c = _b[_i], x = _c[0], y = _c[1], z = _c[2];
|
|
131
|
+
nv.drawPt(x, y, z, draft.penValue);
|
|
132
|
+
}
|
|
133
|
+
nv.refreshDrawing(false, false);
|
|
134
|
+
nv.drawScene();
|
|
135
|
+
}
|
|
136
|
+
export function translateFreehandDraft(draft, delta) {
|
|
137
|
+
return __assign(__assign({}, draft), { strokeVoxels: draft.strokeVoxels.map(function (v) { return translatePt(v, delta); }), bounds: draft.bounds
|
|
138
|
+
? {
|
|
139
|
+
x1: draft.bounds.x1 + delta[0],
|
|
140
|
+
y1: draft.bounds.y1 + delta[1],
|
|
141
|
+
z1: draft.bounds.z1 + delta[2],
|
|
142
|
+
x2: draft.bounds.x2 + delta[0],
|
|
143
|
+
y2: draft.bounds.y2 + delta[1],
|
|
144
|
+
z2: draft.bounds.z2 + delta[2]
|
|
145
|
+
}
|
|
146
|
+
: draft.bounds });
|
|
147
|
+
}
|
|
148
|
+
export function resizeFreehandDraft(draft, cornerIndex, newVox) {
|
|
149
|
+
if (!draft.bounds)
|
|
150
|
+
return draft;
|
|
151
|
+
var axCorSag = draft.axCorSag;
|
|
152
|
+
var b = __assign({}, draft.bounds);
|
|
153
|
+
var corners = boundsToFreehandCorners(b, axCorSag);
|
|
154
|
+
corners[cornerIndex] = __spreadArray([], newVox, true);
|
|
155
|
+
var xs = corners.map(function (c) { return c[0]; });
|
|
156
|
+
var ys = corners.map(function (c) { return c[1]; });
|
|
157
|
+
var zs = corners.map(function (c) { return c[2]; });
|
|
158
|
+
var nextBounds = {
|
|
159
|
+
x1: Math.min.apply(Math, xs),
|
|
160
|
+
y1: Math.min.apply(Math, ys),
|
|
161
|
+
z1: Math.min.apply(Math, zs),
|
|
162
|
+
x2: Math.max.apply(Math, xs),
|
|
163
|
+
y2: Math.max.apply(Math, ys),
|
|
164
|
+
z2: Math.max.apply(Math, zs)
|
|
165
|
+
};
|
|
166
|
+
var oldW = Math.max(1, b.x2 - b.x1);
|
|
167
|
+
var oldH = Math.max(1, b.y2 - b.y1);
|
|
168
|
+
var newW = Math.max(1, nextBounds.x2 - nextBounds.x1);
|
|
169
|
+
var newH = Math.max(1, nextBounds.y2 - nextBounds.y1);
|
|
170
|
+
var scaled = draft.strokeVoxels.map(function (v) {
|
|
171
|
+
var relH = (v[0] - b.x1) / oldW;
|
|
172
|
+
var relV = (v[1] - b.y1) / oldH;
|
|
173
|
+
if (axCorSag === 0) {
|
|
174
|
+
return [
|
|
175
|
+
Math.round(nextBounds.x1 + relH * newW),
|
|
176
|
+
Math.round(nextBounds.y1 + relV * newH),
|
|
177
|
+
v[2],
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
if (axCorSag === 1) {
|
|
181
|
+
return [
|
|
182
|
+
Math.round(nextBounds.x1 + relH * newW),
|
|
183
|
+
v[1],
|
|
184
|
+
Math.round(nextBounds.y1 + relV * newH),
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
return [
|
|
188
|
+
v[0],
|
|
189
|
+
Math.round(nextBounds.x1 + relH * newW),
|
|
190
|
+
Math.round(nextBounds.y1 + relV * newH),
|
|
191
|
+
];
|
|
192
|
+
});
|
|
193
|
+
return __assign(__assign({}, draft), { strokeVoxels: scaled, bounds: nextBounds });
|
|
194
|
+
}
|
|
195
|
+
export function boundsToFreehandCorners(bounds, axCorSag) {
|
|
196
|
+
var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
|
|
197
|
+
if (axCorSag === 0) {
|
|
198
|
+
var z = Math.round((z1 + z2) / 2);
|
|
199
|
+
return [
|
|
200
|
+
[x1, y1, z],
|
|
201
|
+
[x2, y1, z],
|
|
202
|
+
[x1, y2, z],
|
|
203
|
+
[x2, y2, z],
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
if (axCorSag === 1) {
|
|
207
|
+
var y = Math.round((y1 + y2) / 2);
|
|
208
|
+
return [
|
|
209
|
+
[x1, y, z1],
|
|
210
|
+
[x2, y, z1],
|
|
211
|
+
[x1, y, z2],
|
|
212
|
+
[x2, y, z2],
|
|
213
|
+
];
|
|
214
|
+
}
|
|
215
|
+
var x = Math.round((x1 + x2) / 2);
|
|
216
|
+
return [
|
|
217
|
+
[x, y1, z1],
|
|
218
|
+
[x, y2, z1],
|
|
219
|
+
[x, y1, z2],
|
|
220
|
+
[x, y2, z2],
|
|
221
|
+
];
|
|
222
|
+
}
|
|
223
|
+
export function polylineDraftFromNv(nv) {
|
|
224
|
+
var vertices = nv._cloudMrPolylineVertices;
|
|
225
|
+
var baseBitmap = nv._cloudMrPolylineSessionStartBitmap;
|
|
226
|
+
if (!(vertices === null || vertices === void 0 ? void 0 : vertices.length) || vertices.length < 2 || !baseBitmap)
|
|
227
|
+
return null;
|
|
228
|
+
return {
|
|
229
|
+
kind: "polyline",
|
|
230
|
+
vertices: vertices.map(function (v) { return __spreadArray([], v, true); }),
|
|
231
|
+
baseBitmap: new Uint8Array(baseBitmap),
|
|
232
|
+
axCorSag: nv._cloudMrPolylineAxCorSag,
|
|
233
|
+
penValue: nv.opts.penValue
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
export function applyPenDraft(nv, draft, _a) {
|
|
237
|
+
var _b = _a === void 0 ? {} : _a, _c = _b.fillClosed, fillClosed = _c === void 0 ? false : _c;
|
|
238
|
+
if (draft.kind === "polyline") {
|
|
239
|
+
redrawPolylineDraft(nv, draft);
|
|
240
|
+
if (fillClosed && draft.vertices.length >= 3) {
|
|
241
|
+
nv.drawPenAxCorSag = draft.axCorSag;
|
|
242
|
+
nv.drawPenLine(draft.vertices[0], draft.vertices[draft.vertices.length - 1], draft.penValue);
|
|
243
|
+
nv.drawPenFillPts = draft.vertices.map(function (v) { return __spreadArray([], v, true); });
|
|
244
|
+
nv.drawPenFilled();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
redrawFreehandDraft(nv, draft);
|
|
249
|
+
}
|
|
250
|
+
nv.drawAddUndoBitmap(nv.drawFillOverwrites);
|
|
251
|
+
if (typeof nv.onDrawingChanged === "function") {
|
|
252
|
+
nv.onDrawingChanged("draw");
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
export function cancelPenDraft(nv, draft) {
|
|
256
|
+
if (draft === null || draft === void 0 ? void 0 : draft.baseBitmap) {
|
|
257
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
258
|
+
nv.refreshDrawing(true, false);
|
|
259
|
+
nv.drawScene();
|
|
260
|
+
}
|
|
261
|
+
}
|