cloudmr-ux 4.4.2 → 4.4.4
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 +5 -1
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivuePanel.js +20 -10
- package/dist/CmrComponents/niivue-viewer/CloudMrNiivueViewer.js +86 -20
- package/dist/CmrComponents/niivue-viewer/NiivuePatcher.js +38 -0
- package/dist/CmrComponents/niivue-viewer/ShapeDraftOverlay.d.ts +11 -0
- package/dist/CmrComponents/niivue-viewer/ShapeDraftOverlay.js +175 -0
- package/dist/CmrComponents/niivue-viewer/mro-draw-toolkit/MroDrawToolkit.js +6 -3
- package/dist/CmrComponents/niivue-viewer/shapeDraftUtils.d.ts +60 -0
- package/dist/CmrComponents/niivue-viewer/shapeDraftUtils.js +188 -0
- package/dist/CmrComponents/rename/edit.js +11 -2
- package/package.json +1 -1
- /package/dist/CmrComponents/niivue-viewer/{Toolbar.scss → Toolbar.css} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import "./Toolbar.
|
|
2
|
+
import "./Toolbar.css";
|
|
3
3
|
import type { DrawToolkitProps } from "../draw-toolkit/DrawToolkit";
|
|
4
4
|
/** Props for {@link DrawToolkit} minus brush controls when using {@link MroDrawToolkit}. */
|
|
5
5
|
export type CloudMrDrawToolkitProps = Omit<DrawToolkitProps, "brushSize" | "updateBrushSize">;
|
|
@@ -34,5 +34,9 @@ export interface CloudMrNiivuePanelProps {
|
|
|
34
34
|
gamma: number;
|
|
35
35
|
gammaKey: number;
|
|
36
36
|
setGamma: (val: number) => void;
|
|
37
|
+
shapeDraft: import("./shapeDraftUtils").ShapeDraft | null;
|
|
38
|
+
onShapeDraftChange: (draft: import("./shapeDraftUtils").ShapeDraft) => void;
|
|
39
|
+
onApplyShapeDraft: () => void;
|
|
40
|
+
onCancelShapeDraft: () => void;
|
|
37
41
|
}
|
|
38
42
|
export declare function CloudMrNiivuePanel(props: CloudMrNiivuePanelProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -50,11 +50,12 @@ import React from "react";
|
|
|
50
50
|
import { Box } from "@mui/material";
|
|
51
51
|
import { NI_PEN_TYPE } from "./niivuePenType";
|
|
52
52
|
import LocationTable from "../../core/common/components/NiivueTools/components/LocationTable";
|
|
53
|
-
import "./Toolbar.
|
|
53
|
+
import "./Toolbar.css";
|
|
54
54
|
import { NiivueSlicePosition } from "../niivue-slice-position/NiivueSlicePosition";
|
|
55
55
|
import { NiivueContrastAdjustments } from "../niivue-contrast-adjustments/NiivueContrastAdjustments";
|
|
56
56
|
import { NiivueRoiTable } from "../niivue-roi-table/NiivueRoiTable";
|
|
57
57
|
import { MroDrawToolkit } from "./mro-draw-toolkit/MroDrawToolkit";
|
|
58
|
+
import { ShapeDraftOverlay } from "./ShapeDraftOverlay";
|
|
58
59
|
export function CloudMrNiivuePanel(props) {
|
|
59
60
|
var _this = this;
|
|
60
61
|
var canvas = React.useRef(null);
|
|
@@ -105,8 +106,12 @@ export function CloudMrNiivuePanel(props) {
|
|
|
105
106
|
props.resampleImage();
|
|
106
107
|
}, [histogram]);
|
|
107
108
|
function applyDrawShapeTool(tool) {
|
|
109
|
+
if (props.shapeDraft) {
|
|
110
|
+
props.onCancelShapeDraft();
|
|
111
|
+
}
|
|
108
112
|
props.setDrawShapeTool(tool);
|
|
109
113
|
var nv = props.nv;
|
|
114
|
+
nv.opts.deferShapeCommit = tool === "rectangle" || tool === "ellipse";
|
|
110
115
|
nv.opts.penType =
|
|
111
116
|
tool === "rectangle"
|
|
112
117
|
? NI_PEN_TYPE.RECTANGLE
|
|
@@ -137,7 +142,12 @@ export function CloudMrNiivuePanel(props) {
|
|
|
137
142
|
alignItems: "stretch",
|
|
138
143
|
justifyContent: "flex-start",
|
|
139
144
|
mb: { xs: 2, md: 0 }
|
|
140
|
-
} }, { children: [_jsx(MroDrawToolkit, __assign({}, props.drawToolkitProps, { drawShapeTool: props.drawShapeTool, onDrawShapeToolChange: applyDrawShapeTool, onExitDrawMode: function () {
|
|
145
|
+
} }, { children: [_jsx(MroDrawToolkit, __assign({}, props.drawToolkitProps, { drawShapeTool: props.drawShapeTool, onDrawShapeToolChange: applyDrawShapeTool, onExitDrawMode: function () {
|
|
146
|
+
if (props.shapeDraft) {
|
|
147
|
+
props.onCancelShapeDraft();
|
|
148
|
+
}
|
|
149
|
+
props.setDrawShapeTool(null);
|
|
150
|
+
}, shapeDraftActive: props.shapeDraft != null, onApplyShapeDraft: props.onApplyShapeDraft, onCancelShapeDraft: props.onCancelShapeDraft, style: {
|
|
141
151
|
marginBottom: 0,
|
|
142
152
|
width: "100%",
|
|
143
153
|
flexShrink: 0
|
|
@@ -148,7 +158,7 @@ export function CloudMrNiivuePanel(props) {
|
|
|
148
158
|
color: "white",
|
|
149
159
|
background: "black",
|
|
150
160
|
flexShrink: 0
|
|
151
|
-
} }),
|
|
161
|
+
} }), _jsxs(Box, __assign({ sx: {
|
|
152
162
|
position: "relative",
|
|
153
163
|
width: "100%",
|
|
154
164
|
flexShrink: 0,
|
|
@@ -162,13 +172,13 @@ export function CloudMrNiivuePanel(props) {
|
|
|
162
172
|
sm: 400,
|
|
163
173
|
md: 1035
|
|
164
174
|
}
|
|
165
|
-
} }, { children: _jsx("canvas", { id: "niiCanvas", ref: canvas, style: {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
} }, { children: [_jsx("canvas", { id: "niiCanvas", ref: canvas, style: {
|
|
176
|
+
position: "absolute",
|
|
177
|
+
top: 0,
|
|
178
|
+
left: 0,
|
|
179
|
+
width: "100%",
|
|
180
|
+
height: "100%"
|
|
181
|
+
} }), props.shapeDraft && (_jsx(ShapeDraftOverlay, { nv: props.nv, draft: props.shapeDraft, onDraftChange: props.onShapeDraftChange, overlayKey: props.mms }))] }))] })), _jsxs(Box, __assign({ sx: {
|
|
172
182
|
width: {
|
|
173
183
|
xs: "100%",
|
|
174
184
|
md: "35%"
|
|
@@ -168,11 +168,16 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
168
168
|
var _20 = useState({ a: 1, b: 0 }), transformFactors = _20[0], setTransformFactors = _20[1];
|
|
169
169
|
var _21 = useState(false), saving = _21[0], setSaving = _21[1];
|
|
170
170
|
var _22 = useState(null), drawShapeTool = _22[0], setDrawShapeTool = _22[1];
|
|
171
|
+
var _23 = useState(null), shapeDraft = _23[0], setShapeDraft = _23[1];
|
|
172
|
+
var shapeDraftRef = React.useRef(null);
|
|
173
|
+
var drawShapeToolRef = React.useRef(null);
|
|
174
|
+
drawShapeToolRef.current = drawShapeTool;
|
|
175
|
+
shapeDraftRef.current = shapeDraft;
|
|
171
176
|
React.useEffect(function () {
|
|
172
177
|
nv.opts.penBounds = 0;
|
|
173
178
|
}, []);
|
|
174
|
-
var
|
|
175
|
-
var
|
|
179
|
+
var _24 = React.useState(1.0), gamma = _24[0], setGamma = _24[1];
|
|
180
|
+
var _25 = React.useState(0), gammaKey = _25[0], setGammaKey = _25[1];
|
|
176
181
|
// Niivue → React bridge so other places (Toolbar) can force the UI to reset
|
|
177
182
|
nv.onResetGamma = function () {
|
|
178
183
|
setGamma(1.0);
|
|
@@ -217,13 +222,13 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
217
222
|
// setLayers([...nv.volumes])
|
|
218
223
|
// }, [])
|
|
219
224
|
// values dualslider
|
|
220
|
-
var
|
|
225
|
+
var _26 = useState(0), rangeKey = _26[0], setRangeKey = _26[1];
|
|
221
226
|
nv.onResetContrast = function () {
|
|
222
227
|
setRangeKey(rangeKey + 1);
|
|
223
228
|
};
|
|
224
|
-
var
|
|
225
|
-
var
|
|
226
|
-
var
|
|
229
|
+
var _27 = useState([0, 0, 0]), boundMins = _27[0], setBoundMins = _27[1];
|
|
230
|
+
var _28 = useState([1, 1, 1]), boundMaxs = _28[0], setBoundMaxs = _28[1];
|
|
231
|
+
var _29 = useState([0.5, 0.5, 0.5]), mms = _29[0], setMMs = _29[1];
|
|
227
232
|
nv.onImageLoaded = function () {
|
|
228
233
|
var _a, _b;
|
|
229
234
|
var oldCrosshairPos = __spreadArray([], nv.scene.crosshairPos, true);
|
|
@@ -414,8 +419,12 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
414
419
|
// }
|
|
415
420
|
// console.log(nv.scene.pan2Dxyzmm);
|
|
416
421
|
};
|
|
417
|
-
nv.onMouseUp = function (
|
|
418
|
-
if (
|
|
422
|
+
nv.onMouseUp = function () {
|
|
423
|
+
if (nv._cloudMrSuppressDrawingChangedMouseUp) {
|
|
424
|
+
nv._cloudMrSuppressDrawingChangedMouseUp = false;
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (nv.opts.drawingEnabled) {
|
|
419
428
|
setDrawingChanged(true);
|
|
420
429
|
resampleImage();
|
|
421
430
|
}
|
|
@@ -502,7 +511,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
502
511
|
setTextsVisible(true);
|
|
503
512
|
}
|
|
504
513
|
}
|
|
505
|
-
var
|
|
514
|
+
var _30 = useState("pan"), dragMode = _30[0], setDragMode = _30[1];
|
|
506
515
|
function nvSetDragMode(dragMode) {
|
|
507
516
|
switch (dragMode) {
|
|
508
517
|
case "none":
|
|
@@ -711,7 +720,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
711
720
|
nv.opts.crosshairWidth = w;
|
|
712
721
|
nv.drawScene();
|
|
713
722
|
}
|
|
714
|
-
var
|
|
723
|
+
var _31 = useState({}), labelMapping = _31[0], setLabelMapping = _31[1];
|
|
715
724
|
function resampleImage(mapping) {
|
|
716
725
|
if (mapping === void 0) { mapping = labelMapping; }
|
|
717
726
|
var el = typeof document !== "undefined" ? document.getElementById("histoplot") : null;
|
|
@@ -724,11 +733,64 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
724
733
|
setROIs(next);
|
|
725
734
|
}
|
|
726
735
|
}
|
|
736
|
+
function cancelShapeDraft() {
|
|
737
|
+
var draft = shapeDraftRef.current;
|
|
738
|
+
if (!draft)
|
|
739
|
+
return;
|
|
740
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
741
|
+
nv.refreshDrawing(true, false);
|
|
742
|
+
nv.drawScene();
|
|
743
|
+
setShapeDraft(null);
|
|
744
|
+
shapeDraftRef.current = null;
|
|
745
|
+
nv._cloudMrShapeDraftActive = false;
|
|
746
|
+
if (drawShapeToolRef.current) {
|
|
747
|
+
nvSetDrawingEnabled(true);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function applyShapeDraft() {
|
|
751
|
+
if (!shapeDraftRef.current)
|
|
752
|
+
return;
|
|
753
|
+
nv.drawAddUndoBitmap(nv.drawFillOverwrites);
|
|
754
|
+
setDrawingChanged(true);
|
|
755
|
+
setShapeDraft(null);
|
|
756
|
+
shapeDraftRef.current = null;
|
|
757
|
+
nv._cloudMrShapeDraftActive = false;
|
|
758
|
+
if (drawShapeToolRef.current) {
|
|
759
|
+
nvSetDrawingEnabled(true);
|
|
760
|
+
}
|
|
761
|
+
resampleImage();
|
|
762
|
+
}
|
|
763
|
+
function onShapeDraftChange(draft) {
|
|
764
|
+
setShapeDraft(draft);
|
|
765
|
+
shapeDraftRef.current = draft;
|
|
766
|
+
}
|
|
767
|
+
nv.onShapeDraftReady = function (draft) {
|
|
768
|
+
setShapeDraft(draft);
|
|
769
|
+
shapeDraftRef.current = draft;
|
|
770
|
+
nv._cloudMrShapeDraftActive = true;
|
|
771
|
+
nvSetDrawingEnabled(false);
|
|
772
|
+
};
|
|
773
|
+
React.useEffect(function () {
|
|
774
|
+
if (!shapeDraft)
|
|
775
|
+
return undefined;
|
|
776
|
+
var onKeyDown = function (event) {
|
|
777
|
+
if (event.key === "Enter") {
|
|
778
|
+
event.preventDefault();
|
|
779
|
+
applyShapeDraft();
|
|
780
|
+
}
|
|
781
|
+
else if (event.key === "Escape") {
|
|
782
|
+
event.preventDefault();
|
|
783
|
+
cancelShapeDraft();
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
window.addEventListener("keydown", onKeyDown);
|
|
787
|
+
return function () { return window.removeEventListener("keydown", onKeyDown); };
|
|
788
|
+
}, [shapeDraft]);
|
|
727
789
|
function nvUpdateSelectionBoxColor(rgb01) {
|
|
728
790
|
setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
|
|
729
791
|
nv.setSelectionBoxColor(__spreadArray(__spreadArray([], rgb01, true), [0.5], false));
|
|
730
792
|
}
|
|
731
|
-
var
|
|
793
|
+
var _32 = React.useState('axial'), sliceType = _32[0], setSliceType = _32[1];
|
|
732
794
|
function nvUpdateSliceType(newSliceType) {
|
|
733
795
|
setSliceType(newSliceType);
|
|
734
796
|
if (newSliceType === 'axial') {
|
|
@@ -868,14 +930,14 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
868
930
|
return [2 /*return*/];
|
|
869
931
|
});
|
|
870
932
|
}); };
|
|
871
|
-
var
|
|
872
|
-
var
|
|
873
|
-
var
|
|
874
|
-
}), saveConfirmCallback =
|
|
875
|
-
var
|
|
876
|
-
var
|
|
877
|
-
var
|
|
878
|
-
var
|
|
933
|
+
var _33 = useState(''), selectedROI = _33[0], setSelectedDrawingLayer = _33[1];
|
|
934
|
+
var _34 = useState(false), saveDialogOpen = _34[0], setSaveDialogOpen = _34[1];
|
|
935
|
+
var _35 = useState(function () {
|
|
936
|
+
}), saveConfirmCallback = _35[0], setSaveConfirmCallback = _35[1];
|
|
937
|
+
var _36 = useState(false), confirmationOpen = _36[0], setConfirmationOpen = _36[1];
|
|
938
|
+
var _37 = useState(function () { }), warningConfirmationCallback = _37[0], setWarningConfirmationCallback = _37[1];
|
|
939
|
+
var _38 = useState(function () { }), warningCancelCallback = _38[0], setWarningCancelCallback = _38[1];
|
|
940
|
+
var _39 = useState(false), drawingChanged = _39[0], setDrawingChanged = _39[1];
|
|
879
941
|
// When new drawing strokes are made on top of a saved ROI, reset the dropdown
|
|
880
942
|
// so it shows the "ROI Layer" placeholder instead of the stale saved name.
|
|
881
943
|
React.useEffect(function () {
|
|
@@ -1198,6 +1260,10 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1198
1260
|
changesMade: drawingChanged,
|
|
1199
1261
|
// toggleSampleDistribution,
|
|
1200
1262
|
drawUndo: function () {
|
|
1263
|
+
if (shapeDraftRef.current) {
|
|
1264
|
+
cancelShapeDraft();
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1201
1267
|
nv.drawUndo();
|
|
1202
1268
|
resampleImage();
|
|
1203
1269
|
if (nv.drawBitmap && nv.drawBitmap.every(function (v) { return v === 0; })) {
|
|
@@ -1225,7 +1291,7 @@ export default function CloudMrNiivueViewer(props) {
|
|
|
1225
1291
|
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 () {
|
|
1226
1292
|
var _a;
|
|
1227
1293
|
void ((_a = props.refreshPipelineRois) === null || _a === void 0 ? void 0 : _a.call(props));
|
|
1228
|
-
}, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma }, "".concat(selectedVolume))] })));
|
|
1294
|
+
}, gamma: gamma, gammaKey: gammaKey, setGamma: setGamma, shapeDraft: shapeDraft, onShapeDraftChange: onShapeDraftChange, onApplyShapeDraft: applyShapeDraft, onCancelShapeDraft: cancelShapeDraft }, "".concat(selectedVolume))] })));
|
|
1229
1295
|
}
|
|
1230
1296
|
function niiToVolume(nii) {
|
|
1231
1297
|
return {
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* This file patches the original NiiVue library to produce customized behaviors and effects.
|
|
3
3
|
*/
|
|
4
4
|
import { Niivue, NVImage, NVImageFromUrlOptions } from "@niivue/niivue";
|
|
5
|
+
import {
|
|
6
|
+
captureDeferredShapeDraft,
|
|
7
|
+
isDraftTooSmall,
|
|
8
|
+
shouldDeferShapeCommit,
|
|
9
|
+
} from "./shapeDraftUtils.js";
|
|
5
10
|
|
|
6
11
|
/*
|
|
7
12
|
* bitmapOverlay — in-browser state only: remembers each voxel's label *before* Group
|
|
@@ -1399,4 +1404,37 @@ Niivue.prototype.drawCrossLinesMM = function (sliceIndex, axCorSag, axiMM, corMM
|
|
|
1399
1404
|
// );
|
|
1400
1405
|
// gl.bindVertexArray(this.unusedVAO);
|
|
1401
1406
|
// }
|
|
1407
|
+
|
|
1408
|
+
const _drawAddUndoBitmap = Niivue.prototype.drawAddUndoBitmap;
|
|
1409
|
+
Niivue.prototype.drawAddUndoBitmap = function cloudMrDrawAddUndoBitmap(...args) {
|
|
1410
|
+
if (this._cloudMrSkipNextUndoBitmap) {
|
|
1411
|
+
this._cloudMrSkipNextUndoBitmap = false;
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
return _drawAddUndoBitmap.apply(this, args);
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1417
|
+
const _mouseUpListener = Niivue.prototype.mouseUpListener;
|
|
1418
|
+
Niivue.prototype.mouseUpListener = function cloudMrMouseUpListener() {
|
|
1419
|
+
let pendingDraft = null;
|
|
1420
|
+
if (shouldDeferShapeCommit(this)) {
|
|
1421
|
+
this._cloudMrSkipNextUndoBitmap = true;
|
|
1422
|
+
pendingDraft = captureDeferredShapeDraft(this);
|
|
1423
|
+
}
|
|
1424
|
+
_mouseUpListener.call(this);
|
|
1425
|
+
if (!pendingDraft?.baseBitmap) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
if (isDraftTooSmall(pendingDraft.ptA, pendingDraft.ptB)) {
|
|
1429
|
+
this.drawBitmap.set(pendingDraft.baseBitmap);
|
|
1430
|
+
this.refreshDrawing(true, false);
|
|
1431
|
+
this.drawScene();
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
this._cloudMrSuppressDrawingChangedMouseUp = true;
|
|
1435
|
+
if (typeof this.onShapeDraftReady === "function") {
|
|
1436
|
+
this.onShapeDraftReady(pendingDraft);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1402
1440
|
export { Niivue };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Overlay handles for adjusting a rectangle/ellipse draft before commit.
|
|
3
|
+
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
4
|
+
*/
|
|
5
|
+
export function ShapeDraftOverlay({ nv, draft, onDraftChange, overlayKey }: {
|
|
6
|
+
nv: any;
|
|
7
|
+
draft: import('./shapeDraftUtils').ShapeDraft;
|
|
8
|
+
onDraftChange: (d: any) => void;
|
|
9
|
+
overlayKey?: unknown;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
11
|
+
export default ShapeDraftOverlay;
|
|
@@ -0,0 +1,175 @@
|
|
|
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 { boundsToCorners, canvasDeltaToVoxDelta, clientToCanvasPos, normalizeBounds, redrawDraftShape, resizeDraftCorner, translatePt, voxToOverlayPos, voxUnderClient, } from "./shapeDraftUtils";
|
|
24
|
+
var HANDLE_SIZE = 10;
|
|
25
|
+
var ACCENT = "#580f8b";
|
|
26
|
+
/**
|
|
27
|
+
* Overlay handles for adjusting a rectangle/ellipse draft before commit.
|
|
28
|
+
* @param {{ nv: any, draft: import('./shapeDraftUtils').ShapeDraft, onDraftChange: (d: any) => void, overlayKey?: unknown }} props
|
|
29
|
+
*/
|
|
30
|
+
export function ShapeDraftOverlay(_a) {
|
|
31
|
+
var _b;
|
|
32
|
+
var nv = _a.nv, draft = _a.draft, onDraftChange = _a.onDraftChange, overlayKey = _a.overlayKey;
|
|
33
|
+
var dragRef = useRef(null);
|
|
34
|
+
var draftRef = useRef(draft);
|
|
35
|
+
draftRef.current = draft;
|
|
36
|
+
var onPointerMoveRef = useRef(null);
|
|
37
|
+
var finishDragRef = useRef(null);
|
|
38
|
+
var _c = useState(0), setTick = _c[1];
|
|
39
|
+
var bump = useCallback(function () { return setTick(function (t) { return t + 1; }); }, []);
|
|
40
|
+
useEffect(function () {
|
|
41
|
+
var onMove = function () { return bump(); };
|
|
42
|
+
window.addEventListener("resize", onMove);
|
|
43
|
+
return function () { return window.removeEventListener("resize", onMove); };
|
|
44
|
+
}, [bump]);
|
|
45
|
+
var dims = (_b = nv === null || nv === void 0 ? void 0 : nv.back) === null || _b === void 0 ? void 0 : _b.dims;
|
|
46
|
+
var bounds = useMemo(function () {
|
|
47
|
+
if (!dims || !draft)
|
|
48
|
+
return null;
|
|
49
|
+
return normalizeBounds(draft.ptA, draft.ptB, dims);
|
|
50
|
+
}, [dims, draft, overlayKey]);
|
|
51
|
+
var corners = useMemo(function () {
|
|
52
|
+
if (!bounds || draft == null)
|
|
53
|
+
return [];
|
|
54
|
+
return boundsToCorners(bounds, draft.axCorSag);
|
|
55
|
+
}, [bounds, draft === null || draft === void 0 ? void 0 : draft.axCorSag, overlayKey]);
|
|
56
|
+
var cornerCss = useMemo(function () {
|
|
57
|
+
return corners
|
|
58
|
+
.map(function (vox) { return voxToOverlayPos(nv, vox, draft.axCorSag); })
|
|
59
|
+
.filter(Boolean);
|
|
60
|
+
}, [corners, draft === null || draft === void 0 ? void 0 : draft.axCorSag, nv, overlayKey]);
|
|
61
|
+
var centerCss = useMemo(function () {
|
|
62
|
+
if (!bounds || !draft)
|
|
63
|
+
return null;
|
|
64
|
+
var cx = (bounds.x1 + bounds.x2) / 2;
|
|
65
|
+
var cy = (bounds.y1 + bounds.y2) / 2;
|
|
66
|
+
var cz = (bounds.z1 + bounds.z2) / 2;
|
|
67
|
+
return voxToOverlayPos(nv, [cx, cy, cz], draft.axCorSag);
|
|
68
|
+
}, [bounds, draft === null || draft === void 0 ? void 0 : draft.axCorSag, nv, overlayKey]);
|
|
69
|
+
var boxStyle = useMemo(function () {
|
|
70
|
+
if (cornerCss.length < 4)
|
|
71
|
+
return null;
|
|
72
|
+
var xs = cornerCss.map(function (p) { return p.x; });
|
|
73
|
+
var ys = cornerCss.map(function (p) { return p.y; });
|
|
74
|
+
var left = Math.min.apply(Math, xs);
|
|
75
|
+
var top = Math.min.apply(Math, ys);
|
|
76
|
+
var width = Math.max.apply(Math, xs) - left;
|
|
77
|
+
var height = Math.max.apply(Math, ys) - top;
|
|
78
|
+
if (width < 2 || height < 2)
|
|
79
|
+
return null;
|
|
80
|
+
return { left: left, top: top, width: width, height: height };
|
|
81
|
+
}, [cornerCss]);
|
|
82
|
+
var applyDraft = useCallback(function (nextDraft) {
|
|
83
|
+
redrawDraftShape(nv, nextDraft);
|
|
84
|
+
onDraftChange(nextDraft);
|
|
85
|
+
}, [nv, onDraftChange]);
|
|
86
|
+
finishDragRef.current = function () {
|
|
87
|
+
dragRef.current = null;
|
|
88
|
+
if (onPointerMoveRef.current) {
|
|
89
|
+
window.removeEventListener("pointermove", onPointerMoveRef.current);
|
|
90
|
+
}
|
|
91
|
+
if (finishDragRef.current) {
|
|
92
|
+
window.removeEventListener("pointerup", finishDragRef.current);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
onPointerMoveRef.current = function (event) {
|
|
96
|
+
var drag = dragRef.current;
|
|
97
|
+
var currentDraft = draftRef.current;
|
|
98
|
+
if (!drag || !currentDraft)
|
|
99
|
+
return;
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
if (drag.mode === "move") {
|
|
102
|
+
var canvas = nv.canvas || document.getElementById("niiCanvas");
|
|
103
|
+
if (!canvas)
|
|
104
|
+
return;
|
|
105
|
+
var startCanvas = clientToCanvasPos(canvas, drag.startClientX, drag.startClientY);
|
|
106
|
+
var endCanvas = clientToCanvasPos(canvas, event.clientX, event.clientY);
|
|
107
|
+
var delta = canvasDeltaToVoxDelta(nv, startCanvas, endCanvas);
|
|
108
|
+
var next = __assign(__assign({}, currentDraft), { ptA: translatePt(drag.startPtA, delta), ptB: translatePt(drag.startPtB, delta) });
|
|
109
|
+
applyDraft(next);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (drag.mode === "corner") {
|
|
113
|
+
var vox = voxUnderClient(nv, event.clientX, event.clientY);
|
|
114
|
+
if (!vox)
|
|
115
|
+
return;
|
|
116
|
+
var next = resizeDraftCorner(nv, currentDraft, drag.cornerIndex, vox);
|
|
117
|
+
applyDraft(next);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var startDrag = useCallback(function (event, mode, cornerIndex) {
|
|
121
|
+
if (cornerIndex === void 0) { cornerIndex = -1; }
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
event.stopPropagation();
|
|
124
|
+
dragRef.current = {
|
|
125
|
+
mode: mode,
|
|
126
|
+
cornerIndex: cornerIndex,
|
|
127
|
+
startClientX: event.clientX,
|
|
128
|
+
startClientY: event.clientY,
|
|
129
|
+
startPtA: __spreadArray([], draft.ptA, true),
|
|
130
|
+
startPtB: __spreadArray([], draft.ptB, true)
|
|
131
|
+
};
|
|
132
|
+
window.addEventListener("pointermove", onPointerMoveRef.current);
|
|
133
|
+
window.addEventListener("pointerup", finishDragRef.current);
|
|
134
|
+
}, [applyDraft, draft]);
|
|
135
|
+
useEffect(function () { return function () {
|
|
136
|
+
if (finishDragRef.current) {
|
|
137
|
+
finishDragRef.current();
|
|
138
|
+
}
|
|
139
|
+
}; }, []);
|
|
140
|
+
if (!draft || !boxStyle) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
var handleStyle = {
|
|
144
|
+
position: "absolute",
|
|
145
|
+
width: HANDLE_SIZE,
|
|
146
|
+
height: HANDLE_SIZE,
|
|
147
|
+
marginLeft: -HANDLE_SIZE / 2,
|
|
148
|
+
marginTop: -HANDLE_SIZE / 2,
|
|
149
|
+
borderRadius: "50%",
|
|
150
|
+
background: "#fff",
|
|
151
|
+
border: "2px solid ".concat(ACCENT),
|
|
152
|
+
boxSizing: "border-box",
|
|
153
|
+
cursor: "pointer",
|
|
154
|
+
zIndex: 3,
|
|
155
|
+
touchAction: "none"
|
|
156
|
+
};
|
|
157
|
+
return (_jsxs("div", __assign({ style: {
|
|
158
|
+
position: "absolute",
|
|
159
|
+
inset: 0,
|
|
160
|
+
zIndex: 2,
|
|
161
|
+
pointerEvents: "auto",
|
|
162
|
+
overflow: "hidden"
|
|
163
|
+
}, "aria-label": "Adjust ROI shape" }, { children: [_jsx("div", { style: {
|
|
164
|
+
position: "absolute",
|
|
165
|
+
left: boxStyle.left,
|
|
166
|
+
top: boxStyle.top,
|
|
167
|
+
width: boxStyle.width,
|
|
168
|
+
height: boxStyle.height,
|
|
169
|
+
border: "2px dashed ".concat(ACCENT),
|
|
170
|
+
background: "rgba(88, 15, 139, 0.08)",
|
|
171
|
+
boxSizing: "border-box",
|
|
172
|
+
pointerEvents: "none"
|
|
173
|
+
} }), 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: "nwse-resize" }), title: "Resize shape" }, "corner-".concat(i))); })] })));
|
|
174
|
+
}
|
|
175
|
+
export default ShapeDraftOverlay;
|
|
@@ -9,7 +9,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
9
9
|
};
|
|
10
10
|
return __assign.apply(this, arguments);
|
|
11
11
|
};
|
|
12
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
/**
|
|
14
14
|
* MROptimum fork of cloudmr-ux DrawToolkit.
|
|
15
15
|
* Click-away exits drawing mode when you focus elsewhere (slice controls, histogram, etc.),
|
|
@@ -31,7 +31,8 @@ import DeleteIcon from "@mui/icons-material/Delete";
|
|
|
31
31
|
import VisibilityIcon from "@mui/icons-material/Visibility";
|
|
32
32
|
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
|
|
33
33
|
import OpacityIcon from "@mui/icons-material/Opacity";
|
|
34
|
-
import
|
|
34
|
+
import CheckIcon from "@mui/icons-material/Check";
|
|
35
|
+
import CloseIcon from "@mui/icons-material/Close";
|
|
35
36
|
import DrawColorPlatte from "./DrawColorPlatte";
|
|
36
37
|
import EraserPlatte from "./EraserPlatte";
|
|
37
38
|
import MaskPlatte from "./MaskPlatte";
|
|
@@ -141,6 +142,8 @@ export function MroDrawToolkit(props) {
|
|
|
141
142
|
var _a;
|
|
142
143
|
if (clickTargetIsNiivueCanvas(event.target))
|
|
143
144
|
return;
|
|
145
|
+
if (props.shapeDraftActive)
|
|
146
|
+
return;
|
|
144
147
|
setExpandedOption("n");
|
|
145
148
|
setExpandOpacityOptions(false);
|
|
146
149
|
props.setDrawingEnabled(false);
|
|
@@ -160,7 +163,7 @@ export function MroDrawToolkit(props) {
|
|
|
160
163
|
alignItems: "center",
|
|
161
164
|
gap: 4,
|
|
162
165
|
overflow: "visible"
|
|
163
|
-
} }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "r" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled })] })), _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 })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "e" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: toolBtnSx }, { children: filled || expandedOption !== "e" ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Tooltip, __assign({ title: "Save screenshot" }, { children: _jsx("span", { children: _jsx(IconButton, __assign({ "aria-label": "capture", size: "small", disabled: !vol, onClick: function () { return vol && props.nv.saveScene("".concat(vol.name, "_drawing.png")); }, sx: toolBtnSx }, { children: _jsx(CameraAltIcon, { sx: { color: ICON_COLOR } }) })) }) })), _jsx(Tooltip, __assign({ title: "Clear drawing" }, { children: _jsx(IconButton, __assign({ "aria-label": "delete", size: "small", onClick: function () {
|
|
166
|
+
} }, { children: [_jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "d" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Pen" }, { children: _jsx(IconButton, __assign({ "aria-label": "pen", size: "small", onClick: clickPen, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("pen")) }, { children: _jsx(DrawIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "d", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "r" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Rectangle" }, { children: _jsx(IconButton, __assign({ "aria-label": "rectangle", size: "small", onClick: clickRectangle, sx: __assign(__assign({}, toolBtnSx), shapeSelectedSx("rectangle")) }, { children: _jsx(CropSquareOutlinedIcon, { sx: { color: "inherit" } }) })) })), _jsx(DrawColorPlatte, { expanded: expandedOption === "r", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled })] })), _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 })] })), _jsxs(Box, __assign({ sx: { position: "relative", zIndex: expandedOption === "e" ? 1600 : "auto", display: "inline-flex", alignItems: "center" } }, { children: [_jsx(Tooltip, __assign({ title: "Eraser" }, { children: _jsx(IconButton, __assign({ "aria-label": "erase", size: "small", onClick: clickEraser, sx: toolBtnSx }, { children: filled || expandedOption !== "e" ? (_jsx(EraserIcon, {})) : (_jsx(AutoFixNormalOutlinedIcon, { sx: { color: ICON_COLOR } })) })) })), _jsx(EraserPlatte, { expandEraseOptions: expandedOption === "e", updateDrawPen: props.updateDrawPen, setDrawingEnabled: props.setDrawingEnabled })] })), _jsx(Tooltip, __assign({ title: "Undo" }, { children: _jsx(IconButton, __assign({ "aria-label": "revert", size: "small", onClick: function () { return props.drawUndo(); }, sx: toolBtnSx }, { children: _jsx(ReplyIcon, { sx: { color: ICON_COLOR } }) })) })), props.shapeDraftActive && (_jsxs(_Fragment, { children: [_jsx(Tooltip, __assign({ title: "Apply shape (Enter)" }, { children: _jsx(IconButton, __assign({ "aria-label": "apply shape", size: "small", onClick: function () { var _a; return (_a = props.onApplyShapeDraft) === null || _a === void 0 ? void 0 : _a.call(props); }, sx: __assign(__assign({}, toolBtnSx), { backgroundColor: "rgba(88, 15, 139, 0.12)", color: "#580f8b" }) }, { children: _jsx(CheckIcon, { sx: { color: "inherit" } }) })) })), _jsx(Tooltip, __assign({ title: "Cancel shape (Esc)" }, { children: _jsx(IconButton, __assign({ "aria-label": "cancel shape", size: "small", onClick: function () { var _a; return (_a = props.onCancelShapeDraft) === null || _a === void 0 ? void 0 : _a.call(props); }, sx: toolBtnSx }, { children: _jsx(CloseIcon, { sx: { color: ICON_COLOR } }) })) })), _jsx(Typography, __assign({ component: "span", sx: { fontSize: "0.72rem", color: "#580f8b", userSelect: "none", ml: 0.5 } }, { children: "Adjust shape, then Apply" }))] })), _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 () {
|
|
164
167
|
props.nv.clearDrawing();
|
|
165
168
|
props.resampleImage();
|
|
166
169
|
props.setDrawingChanged(false);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** @typedef {'rectangle' | 'ellipse'} ShapeDraftKind */
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {Object} ShapeDraft
|
|
4
|
+
* @property {[number, number, number]} ptA
|
|
5
|
+
* @property {[number, number, number]} ptB
|
|
6
|
+
* @property {number} penValue
|
|
7
|
+
* @property {number} axCorSag
|
|
8
|
+
* @property {number} penType
|
|
9
|
+
* @property {Uint8Array} baseBitmap
|
|
10
|
+
*/
|
|
11
|
+
export function penTypeToKind(penType: any): "rectangle" | "ellipse";
|
|
12
|
+
export function normalizeBounds(ptA: any, ptB: any, dims: any): {
|
|
13
|
+
x1: number;
|
|
14
|
+
y1: number;
|
|
15
|
+
z1: number;
|
|
16
|
+
x2: number;
|
|
17
|
+
y2: number;
|
|
18
|
+
z2: number;
|
|
19
|
+
};
|
|
20
|
+
/** @returns {[number, number, number][]} four corners on the draft slice plane */
|
|
21
|
+
export function boundsToCorners(bounds: any, axCorSag: any): [number, number, number][];
|
|
22
|
+
export function cornersToPtAB(corners: any): {
|
|
23
|
+
ptA: any[];
|
|
24
|
+
ptB: any[];
|
|
25
|
+
};
|
|
26
|
+
export function isDraftTooSmall(ptA: any, ptB: any): boolean;
|
|
27
|
+
/** @param {ShapeDraft} draft */
|
|
28
|
+
export function redrawDraftShape(nv: any, draft: ShapeDraft): void;
|
|
29
|
+
export function clientToCanvasPos(canvas: any, clientX: any, clientY: any): number[];
|
|
30
|
+
export function canvasDeltaToVoxDelta(nv: any, startCanvas: any, endCanvas: any): number[];
|
|
31
|
+
export function translatePt(pt: any, delta: any): any[];
|
|
32
|
+
export function voxUnderClient(nv: any, clientX: any, clientY: any): any[] | null;
|
|
33
|
+
/** @returns {{ x: number, y: number } | null} CSS pixels relative to canvas element */
|
|
34
|
+
export function voxToOverlayPos(nv: any, vox: any, axCorSag: any): {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
} | null;
|
|
38
|
+
/**
|
|
39
|
+
* Move one corner while keeping the opposite corner fixed.
|
|
40
|
+
* @param {number} cornerIndex 0..3 matching boundsToCorners order
|
|
41
|
+
*/
|
|
42
|
+
export function resizeDraftCorner(nv: any, draft: any, cornerIndex: number, newVox: any): any;
|
|
43
|
+
export function captureDeferredShapeDraft(nv: any): {
|
|
44
|
+
ptA: any[];
|
|
45
|
+
ptB: any[];
|
|
46
|
+
penValue: any;
|
|
47
|
+
axCorSag: any;
|
|
48
|
+
penType: any;
|
|
49
|
+
baseBitmap: Uint8Array | null;
|
|
50
|
+
};
|
|
51
|
+
export function shouldDeferShapeCommit(nv: any): any;
|
|
52
|
+
export type ShapeDraftKind = 'rectangle' | 'ellipse';
|
|
53
|
+
export type ShapeDraft = {
|
|
54
|
+
ptA: [number, number, number];
|
|
55
|
+
ptB: [number, number, number];
|
|
56
|
+
penValue: number;
|
|
57
|
+
axCorSag: number;
|
|
58
|
+
penType: number;
|
|
59
|
+
baseBitmap: Uint8Array;
|
|
60
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
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 { NI_PEN_TYPE } from "./niivuePenType";
|
|
22
|
+
/** @typedef {'rectangle' | 'ellipse'} ShapeDraftKind */
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} ShapeDraft
|
|
25
|
+
* @property {[number, number, number]} ptA
|
|
26
|
+
* @property {[number, number, number]} ptB
|
|
27
|
+
* @property {number} penValue
|
|
28
|
+
* @property {number} axCorSag
|
|
29
|
+
* @property {number} penType
|
|
30
|
+
* @property {Uint8Array} baseBitmap
|
|
31
|
+
*/
|
|
32
|
+
export function penTypeToKind(penType) {
|
|
33
|
+
return penType === NI_PEN_TYPE.ELLIPSE ? "ellipse" : "rectangle";
|
|
34
|
+
}
|
|
35
|
+
export function normalizeBounds(ptA, ptB, dims) {
|
|
36
|
+
var dx = dims[1];
|
|
37
|
+
var dy = dims[2];
|
|
38
|
+
var dz = dims[3];
|
|
39
|
+
return {
|
|
40
|
+
x1: Math.min(Math.max(Math.min(ptA[0], ptB[0]), 0), dx - 1),
|
|
41
|
+
y1: Math.min(Math.max(Math.min(ptA[1], ptB[1]), 0), dy - 1),
|
|
42
|
+
z1: Math.min(Math.max(Math.min(ptA[2], ptB[2]), 0), dz - 1),
|
|
43
|
+
x2: Math.min(Math.max(Math.max(ptA[0], ptB[0]), 0), dx - 1),
|
|
44
|
+
y2: Math.min(Math.max(Math.max(ptA[1], ptB[1]), 0), dy - 1),
|
|
45
|
+
z2: Math.min(Math.max(Math.max(ptA[2], ptB[2]), 0), dz - 1)
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** @returns {[number, number, number][]} four corners on the draft slice plane */
|
|
49
|
+
export function boundsToCorners(bounds, axCorSag) {
|
|
50
|
+
var x1 = bounds.x1, y1 = bounds.y1, z1 = bounds.z1, x2 = bounds.x2, y2 = bounds.y2, z2 = bounds.z2;
|
|
51
|
+
if (axCorSag === 0) {
|
|
52
|
+
var z = Math.round((z1 + z2) / 2);
|
|
53
|
+
return [
|
|
54
|
+
[x1, y1, z],
|
|
55
|
+
[x2, y1, z],
|
|
56
|
+
[x1, y2, z],
|
|
57
|
+
[x2, y2, z],
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
if (axCorSag === 1) {
|
|
61
|
+
var y = Math.round((y1 + y2) / 2);
|
|
62
|
+
return [
|
|
63
|
+
[x1, y, z1],
|
|
64
|
+
[x2, y, z1],
|
|
65
|
+
[x1, y, z2],
|
|
66
|
+
[x2, y, z2],
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
var x = Math.round((x1 + x2) / 2);
|
|
70
|
+
return [
|
|
71
|
+
[x, y1, z1],
|
|
72
|
+
[x, y2, z1],
|
|
73
|
+
[x, y1, z2],
|
|
74
|
+
[x, y2, z2],
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
export function cornersToPtAB(corners) {
|
|
78
|
+
return {
|
|
79
|
+
ptA: __spreadArray([], corners[0], true),
|
|
80
|
+
ptB: __spreadArray([], corners[3], true)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export function isDraftTooSmall(ptA, ptB) {
|
|
84
|
+
return (Math.abs(ptA[0] - ptB[0]) < 1 &&
|
|
85
|
+
Math.abs(ptA[1] - ptB[1]) < 1 &&
|
|
86
|
+
Math.abs(ptA[2] - ptB[2]) < 1);
|
|
87
|
+
}
|
|
88
|
+
/** @param {ShapeDraft} draft */
|
|
89
|
+
export function redrawDraftShape(nv, draft) {
|
|
90
|
+
if (!(nv === null || nv === void 0 ? void 0 : nv.drawBitmap) || !(draft === null || draft === void 0 ? void 0 : draft.baseBitmap))
|
|
91
|
+
return;
|
|
92
|
+
nv.drawBitmap.set(draft.baseBitmap);
|
|
93
|
+
nv.drawPenAxCorSag = draft.axCorSag;
|
|
94
|
+
if (draft.penType === NI_PEN_TYPE.RECTANGLE) {
|
|
95
|
+
nv.drawRectangleMask(draft.ptA, draft.ptB, draft.penValue);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
nv.drawEllipseMask(draft.ptA, draft.ptB, draft.penValue);
|
|
99
|
+
}
|
|
100
|
+
nv.refreshDrawing(false, false);
|
|
101
|
+
nv.drawScene();
|
|
102
|
+
}
|
|
103
|
+
export function clientToCanvasPos(canvas, clientX, clientY) {
|
|
104
|
+
var rect = canvas.getBoundingClientRect();
|
|
105
|
+
var scaleX = canvas.width / rect.width;
|
|
106
|
+
var scaleY = canvas.height / rect.height;
|
|
107
|
+
return [(clientX - rect.left) * scaleX, (clientY - rect.top) * scaleY];
|
|
108
|
+
}
|
|
109
|
+
export function canvasDeltaToVoxDelta(nv, startCanvas, endCanvas) {
|
|
110
|
+
var f0 = nv.canvasPos2frac(startCanvas);
|
|
111
|
+
var f1 = nv.canvasPos2frac(endCanvas);
|
|
112
|
+
if (!f0 || !f1 || f0[0] < 0 || f1[0] < 0) {
|
|
113
|
+
return [0, 0, 0];
|
|
114
|
+
}
|
|
115
|
+
var v0 = nv.frac2vox(f0);
|
|
116
|
+
var v1 = nv.frac2vox(f1);
|
|
117
|
+
return [v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]];
|
|
118
|
+
}
|
|
119
|
+
export function translatePt(pt, delta) {
|
|
120
|
+
return [pt[0] + delta[0], pt[1] + delta[1], pt[2] + delta[2]];
|
|
121
|
+
}
|
|
122
|
+
export function voxUnderClient(nv, clientX, clientY) {
|
|
123
|
+
var canvas = nv.canvas || document.getElementById("niiCanvas");
|
|
124
|
+
if (!canvas)
|
|
125
|
+
return null;
|
|
126
|
+
var canvasPos = clientToCanvasPos(canvas, clientX, clientY);
|
|
127
|
+
var frac = nv.canvasPos2frac(canvasPos);
|
|
128
|
+
if (!frac || frac[0] < 0)
|
|
129
|
+
return null;
|
|
130
|
+
var vox = nv.frac2vox(frac);
|
|
131
|
+
return [vox[0], vox[1], vox[2]];
|
|
132
|
+
}
|
|
133
|
+
/** @returns {{ x: number, y: number } | null} CSS pixels relative to canvas element */
|
|
134
|
+
export function voxToOverlayPos(nv, vox, axCorSag) {
|
|
135
|
+
var canvas = nv.canvas || document.getElementById("niiCanvas");
|
|
136
|
+
if (!canvas)
|
|
137
|
+
return null;
|
|
138
|
+
var frac = nv.vox2frac(vox);
|
|
139
|
+
var hit = nv.frac2canvasPosWithTile(frac, axCorSag);
|
|
140
|
+
if (!hit)
|
|
141
|
+
return null;
|
|
142
|
+
var rect = canvas.getBoundingClientRect();
|
|
143
|
+
return {
|
|
144
|
+
x: (hit.pos[0] / canvas.width) * rect.width,
|
|
145
|
+
y: (hit.pos[1] / canvas.height) * rect.height
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Move one corner while keeping the opposite corner fixed.
|
|
150
|
+
* @param {number} cornerIndex 0..3 matching boundsToCorners order
|
|
151
|
+
*/
|
|
152
|
+
export function resizeDraftCorner(nv, draft, cornerIndex, newVox) {
|
|
153
|
+
var _a;
|
|
154
|
+
var dims = (_a = nv.back) === null || _a === void 0 ? void 0 : _a.dims;
|
|
155
|
+
if (!dims)
|
|
156
|
+
return draft;
|
|
157
|
+
var bounds = normalizeBounds(draft.ptA, draft.ptB, dims);
|
|
158
|
+
var corners = boundsToCorners(bounds, draft.axCorSag);
|
|
159
|
+
corners[cornerIndex] = __spreadArray([], newVox, true);
|
|
160
|
+
var next = cornersToPtAB(corners);
|
|
161
|
+
return __assign(__assign({}, draft), { ptA: next.ptA, ptB: next.ptB });
|
|
162
|
+
}
|
|
163
|
+
export function captureDeferredShapeDraft(nv) {
|
|
164
|
+
var frac = nv.canvasPos2frac(nv.mousePos);
|
|
165
|
+
var ptB = __spreadArray([], nv.drawShapeStartLocation, true);
|
|
166
|
+
if (frac && frac[0] >= 0) {
|
|
167
|
+
var vox = nv.frac2vox(frac);
|
|
168
|
+
ptB = [vox[0], vox[1], vox[2]];
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
ptA: __spreadArray([], nv.drawShapeStartLocation, true),
|
|
172
|
+
ptB: ptB,
|
|
173
|
+
penValue: nv.opts.penValue,
|
|
174
|
+
axCorSag: nv.drawPenAxCorSag,
|
|
175
|
+
penType: nv.opts.penType,
|
|
176
|
+
baseBitmap: nv.drawShapePreviewBitmap
|
|
177
|
+
? new Uint8Array(nv.drawShapePreviewBitmap)
|
|
178
|
+
: null
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export function shouldDeferShapeCommit(nv) {
|
|
182
|
+
var penType = nv.opts.penType;
|
|
183
|
+
return (nv.opts.deferShapeCommit &&
|
|
184
|
+
nv.opts.drawingEnabled &&
|
|
185
|
+
!Number.isNaN(nv.drawShapeStartLocation[0]) &&
|
|
186
|
+
(penType === NI_PEN_TYPE.RECTANGLE || penType === NI_PEN_TYPE.ELLIPSE) &&
|
|
187
|
+
nv.drawShapePreviewBitmap);
|
|
188
|
+
}
|
|
@@ -47,7 +47,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
47
47
|
};
|
|
48
48
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
49
49
|
import * as React from 'react';
|
|
50
|
-
import { Typography } from '@mui/material';
|
|
50
|
+
import { Typography, Box } from '@mui/material';
|
|
51
51
|
import TextField from '@mui/material/TextField';
|
|
52
52
|
import Dialog from '@mui/material/Dialog';
|
|
53
53
|
import DialogActions from '@mui/material/DialogActions';
|
|
@@ -114,5 +114,14 @@ export default function CmrNameDialog(props) {
|
|
|
114
114
|
// type="file"
|
|
115
115
|
value: text, onFocus: function (event) {
|
|
116
116
|
event.target.select();
|
|
117
|
-
}, fullWidth: true, inputProps: { style: { fontSize: "16px" } }, variant: "standard", onChange: handleTextFieldChange, error: error, helperText: helperText }), isDemoData !== undefined && (_jsx(
|
|
117
|
+
}, fullWidth: true, inputProps: { style: { fontSize: "16px" } }, variant: "standard", onChange: handleTextFieldChange, error: error, helperText: helperText }), isDemoData !== undefined && (_jsx(Box, __assign({ className: "cmr-name-dialog-demo-data", sx: {
|
|
118
|
+
mt: 2,
|
|
119
|
+
display: 'flex',
|
|
120
|
+
alignItems: 'center',
|
|
121
|
+
'& .MuiFormControlLabel-root': {
|
|
122
|
+
margin: 0,
|
|
123
|
+
marginLeft: 0,
|
|
124
|
+
alignItems: 'center'
|
|
125
|
+
}
|
|
126
|
+
} }, { children: _jsx(CmrCheckbox, __assign({ checked: demoDataChecked, checkedColor: checkboxCheckedColor, onChange: function (e) { return setDemoDataChecked(e.target.checked); } }, { children: "Demo Data" })) })))] }), _jsxs(DialogActions, { children: [_jsx(CmrButton, __assign({ variant: "outlined", onClick: handleClose }, { children: "Cancel" })), _jsx(CmrButton, __assign({ variant: "contained", color: 'primary', onClick: handleConfirm }, { children: "Confirm" }))] })] })) }));
|
|
118
127
|
}
|
package/package.json
CHANGED
|
File without changes
|