copper3d 3.0.1 → 3.1.0
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/README.md +197 -875
- package/dist/Utils/segmentation/CanvasState.d.ts +31 -0
- package/dist/Utils/segmentation/CanvasState.js +167 -0
- package/dist/Utils/segmentation/CanvasState.js.map +1 -0
- package/dist/Utils/segmentation/DragOperator.d.ts +10 -2
- package/dist/Utils/segmentation/DragOperator.js +16 -8
- package/dist/Utils/segmentation/DragOperator.js.map +1 -1
- package/dist/Utils/segmentation/DrawToolCore.d.ts +64 -35
- package/dist/Utils/segmentation/DrawToolCore.js +332 -584
- package/dist/Utils/segmentation/DrawToolCore.js.map +1 -1
- package/dist/Utils/segmentation/NrrdTools.d.ts +117 -353
- package/dist/Utils/segmentation/NrrdTools.js +622 -1081
- package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
- package/dist/Utils/segmentation/RenderingUtils.d.ts +73 -0
- package/dist/Utils/segmentation/RenderingUtils.js +180 -0
- package/dist/Utils/segmentation/RenderingUtils.js.map +1 -0
- package/dist/Utils/segmentation/core/MaskVolume.js +23 -2
- package/dist/Utils/segmentation/core/MaskVolume.js.map +1 -1
- package/dist/Utils/segmentation/core/UndoManager.d.ts +1 -1
- package/dist/Utils/segmentation/core/UndoManager.js +1 -1
- package/dist/Utils/segmentation/core/index.d.ts +1 -3
- package/dist/Utils/segmentation/core/index.js +0 -1
- package/dist/Utils/segmentation/core/index.js.map +1 -1
- package/dist/Utils/segmentation/core/types.d.ts +384 -0
- package/dist/Utils/segmentation/core/types.js +25 -24
- package/dist/Utils/segmentation/core/types.js.map +1 -1
- package/dist/Utils/segmentation/coreTools/GuiState.d.ts +22 -0
- package/dist/Utils/segmentation/coreTools/GuiState.js +48 -0
- package/dist/Utils/segmentation/coreTools/GuiState.js.map +1 -0
- package/dist/Utils/segmentation/coreTools/NrrdState.d.ts +25 -0
- package/dist/Utils/segmentation/coreTools/NrrdState.js +85 -0
- package/dist/Utils/segmentation/coreTools/NrrdState.js.map +1 -0
- package/dist/Utils/segmentation/coreTools/divControlTools.d.ts +1 -1
- package/dist/Utils/segmentation/coreTools/divControlTools.js +1 -1
- package/dist/Utils/segmentation/coreTools/divControlTools.js.map +1 -1
- package/dist/Utils/segmentation/coreTools/gui.d.ts +12 -12
- package/dist/Utils/segmentation/coreTools/gui.js +81 -107
- package/dist/Utils/segmentation/coreTools/gui.js.map +1 -1
- package/dist/Utils/segmentation/eventRouter/EventRouter.d.ts +2 -1
- package/dist/Utils/segmentation/eventRouter/EventRouter.js +15 -10
- package/dist/Utils/segmentation/eventRouter/EventRouter.js.map +1 -1
- package/dist/Utils/segmentation/eventRouter/types.d.ts +3 -3
- package/dist/Utils/segmentation/tools/BaseTool.d.ts +8 -5
- package/dist/Utils/segmentation/tools/BaseTool.js +1 -2
- package/dist/Utils/segmentation/tools/BaseTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/ContrastTool.d.ts +3 -6
- package/dist/Utils/segmentation/tools/ContrastTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/CrosshairTool.d.ts +11 -1
- package/dist/Utils/segmentation/tools/CrosshairTool.js +50 -25
- package/dist/Utils/segmentation/tools/CrosshairTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/DataLoader.d.ts +33 -0
- package/dist/Utils/segmentation/tools/DataLoader.js +159 -0
- package/dist/Utils/segmentation/tools/DataLoader.js.map +1 -0
- package/dist/Utils/segmentation/tools/DragSliceTool.d.ts +3 -10
- package/dist/Utils/segmentation/tools/DragSliceTool.js +63 -53
- package/dist/Utils/segmentation/tools/DragSliceTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/DrawingTool.d.ts +85 -0
- package/dist/Utils/segmentation/tools/DrawingTool.js +270 -0
- package/dist/Utils/segmentation/tools/DrawingTool.js.map +1 -0
- package/dist/Utils/segmentation/tools/EraserTool.js +8 -8
- package/dist/Utils/segmentation/tools/EraserTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/ImageStoreHelper.d.ts +13 -33
- package/dist/Utils/segmentation/tools/ImageStoreHelper.js +39 -82
- package/dist/Utils/segmentation/tools/ImageStoreHelper.js.map +1 -1
- package/dist/Utils/segmentation/tools/LayerChannelManager.d.ts +52 -0
- package/dist/Utils/segmentation/tools/LayerChannelManager.js +175 -0
- package/dist/Utils/segmentation/tools/LayerChannelManager.js.map +1 -0
- package/dist/Utils/segmentation/tools/PanTool.d.ts +47 -0
- package/dist/Utils/segmentation/tools/PanTool.js +90 -0
- package/dist/Utils/segmentation/tools/PanTool.js.map +1 -0
- package/dist/Utils/segmentation/tools/SliceRenderPipeline.d.ts +80 -0
- package/dist/Utils/segmentation/tools/SliceRenderPipeline.js +355 -0
- package/dist/Utils/segmentation/tools/SliceRenderPipeline.js.map +1 -0
- package/dist/Utils/segmentation/tools/SphereTool.d.ts +223 -10
- package/dist/Utils/segmentation/tools/SphereTool.js +512 -47
- package/dist/Utils/segmentation/tools/SphereTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/ToolHost.d.ts +74 -0
- package/dist/Utils/segmentation/tools/ToolHost.js +10 -0
- package/dist/Utils/segmentation/tools/ToolHost.js.map +1 -0
- package/dist/Utils/segmentation/tools/ZoomTool.d.ts +2 -6
- package/dist/Utils/segmentation/tools/ZoomTool.js +3 -3
- package/dist/Utils/segmentation/tools/ZoomTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/index.d.ts +8 -6
- package/dist/Utils/segmentation/tools/index.js +10 -1
- package/dist/Utils/segmentation/tools/index.js.map +1 -1
- package/dist/Utils/workers/reformatSaveDataWorker.d.ts +1 -1
- package/dist/bundle.esm.js +3729 -3017
- package/dist/bundle.umd.js +3729 -3017
- package/dist/index.d.ts +6 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/Utils/segmentation/CanvasState.d.ts +31 -0
- package/dist/types/Utils/segmentation/DragOperator.d.ts +10 -2
- package/dist/types/Utils/segmentation/DrawToolCore.d.ts +64 -35
- package/dist/types/Utils/segmentation/NrrdTools.d.ts +117 -353
- package/dist/types/Utils/segmentation/RenderingUtils.d.ts +73 -0
- package/dist/types/Utils/segmentation/core/UndoManager.d.ts +1 -1
- package/dist/types/Utils/segmentation/core/index.d.ts +1 -3
- package/dist/types/Utils/segmentation/core/types.d.ts +384 -0
- package/dist/types/Utils/segmentation/coreTools/GuiState.d.ts +22 -0
- package/dist/types/Utils/segmentation/coreTools/NrrdState.d.ts +25 -0
- package/dist/types/Utils/segmentation/coreTools/divControlTools.d.ts +1 -1
- package/dist/types/Utils/segmentation/coreTools/gui.d.ts +12 -12
- package/dist/types/Utils/segmentation/eventRouter/EventRouter.d.ts +2 -1
- package/dist/types/Utils/segmentation/eventRouter/types.d.ts +3 -3
- package/dist/types/Utils/segmentation/tools/BaseTool.d.ts +8 -5
- package/dist/types/Utils/segmentation/tools/ContrastTool.d.ts +3 -6
- package/dist/types/Utils/segmentation/tools/CrosshairTool.d.ts +11 -1
- package/dist/types/Utils/segmentation/tools/DataLoader.d.ts +33 -0
- package/dist/types/Utils/segmentation/tools/DragSliceTool.d.ts +3 -10
- package/dist/types/Utils/segmentation/tools/DrawingTool.d.ts +85 -0
- package/dist/types/Utils/segmentation/tools/ImageStoreHelper.d.ts +13 -33
- package/dist/types/Utils/segmentation/tools/LayerChannelManager.d.ts +52 -0
- package/dist/types/Utils/segmentation/tools/PanTool.d.ts +47 -0
- package/dist/types/Utils/segmentation/tools/SliceRenderPipeline.d.ts +80 -0
- package/dist/types/Utils/segmentation/tools/SphereTool.d.ts +223 -10
- package/dist/types/Utils/segmentation/tools/ToolHost.d.ts +74 -0
- package/dist/types/Utils/segmentation/tools/ZoomTool.d.ts +2 -6
- package/dist/types/Utils/segmentation/tools/index.d.ts +8 -6
- package/dist/types/Utils/workers/reformatSaveDataWorker.d.ts +1 -1
- package/dist/types/index.d.ts +6 -3
- package/package.json +1 -1
- package/dist/Utils/segmentation/CommToolsData.d.ts +0 -177
- package/dist/Utils/segmentation/CommToolsData.js +0 -548
- package/dist/Utils/segmentation/CommToolsData.js.map +0 -1
- package/dist/Utils/segmentation/core/MigrationUtils.d.ts +0 -97
- package/dist/Utils/segmentation/core/MigrationUtils.js +0 -163
- package/dist/Utils/segmentation/core/MigrationUtils.js.map +0 -1
- package/dist/Utils/segmentation/coreTools/coreType.d.ts +0 -363
- package/dist/Utils/segmentation/coreTools/coreType.js +0 -3
- package/dist/Utils/segmentation/coreTools/coreType.js.map +0 -1
- package/dist/types/Utils/segmentation/CommToolsData.d.ts +0 -177
- package/dist/types/Utils/segmentation/core/MigrationUtils.d.ts +0 -97
- package/dist/types/Utils/segmentation/coreTools/coreType.d.ts +0 -363
|
@@ -1,17 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { RenderingUtils } from "./RenderingUtils";
|
|
2
|
+
import { switchPencilIcon } from "../utils";
|
|
3
3
|
import { EventRouter } from "./eventRouter";
|
|
4
4
|
import { SphereTool } from "./tools/SphereTool";
|
|
5
5
|
import { CrosshairTool } from "./tools/CrosshairTool";
|
|
6
6
|
import { ContrastTool } from "./tools/ContrastTool";
|
|
7
7
|
import { ZoomTool } from "./tools/ZoomTool";
|
|
8
8
|
import { EraserTool } from "./tools/EraserTool";
|
|
9
|
+
import { PanTool } from "./tools/PanTool";
|
|
10
|
+
import { DrawingTool } from "./tools/DrawingTool";
|
|
9
11
|
import { ImageStoreHelper } from "./tools/ImageStoreHelper";
|
|
10
12
|
import { UndoManager } from "./core";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* DrawToolCore — Tool orchestration and event routing.
|
|
15
|
+
*
|
|
16
|
+
* Previously extended CommToolsData; now uses composition:
|
|
17
|
+
* - `state: CanvasState` — pure state container
|
|
18
|
+
* - `renderer: RenderingUtils` — rendering / slice-buffer helpers
|
|
19
|
+
*
|
|
20
|
+
* Created by NrrdTools which passes in a shared CanvasState.
|
|
21
|
+
*/
|
|
22
|
+
export class DrawToolCore {
|
|
23
|
+
constructor(container, state) {
|
|
15
24
|
this.drawingPrameters = {
|
|
16
25
|
handleOnDrawingMouseDown: (ev) => { },
|
|
17
26
|
handleOnDrawingMouseMove: (ev) => { },
|
|
@@ -37,22 +46,12 @@ export class DrawToolCore extends CommToolsData {
|
|
|
37
46
|
this.eraserUrls = [];
|
|
38
47
|
this.pencilUrls = [];
|
|
39
48
|
this.undoManager = new UndoManager();
|
|
40
|
-
/**
|
|
41
|
-
this.
|
|
42
|
-
|
|
43
|
-
this.
|
|
44
|
-
// Centralized event router
|
|
45
|
-
this.eventRouter = null;
|
|
49
|
+
/** Slice index recorded when paintOnCanvas() starts, guards stale-click */
|
|
50
|
+
this.paintSliceIndex = 0;
|
|
51
|
+
/** Wheel event dispatch mode — replaces manual wheel add/remove (Phase 2) */
|
|
52
|
+
this.activeWheelMode = 'zoom';
|
|
46
53
|
// need to return to parent
|
|
47
54
|
this.start = () => { };
|
|
48
|
-
/*************************************May consider to move outside *******************************************/
|
|
49
|
-
this.drawLine = (x1, y1, x2, y2) => {
|
|
50
|
-
this.protectedData.ctxes.drawingCtx.beginPath();
|
|
51
|
-
this.protectedData.ctxes.drawingCtx.moveTo(x1, y1);
|
|
52
|
-
this.protectedData.ctxes.drawingCtx.lineTo(x2, y2);
|
|
53
|
-
this.protectedData.ctxes.drawingCtx.strokeStyle = this.gui_states.color;
|
|
54
|
-
this.protectedData.ctxes.drawingCtx.stroke();
|
|
55
|
-
};
|
|
56
55
|
this.configContrastDragMode = () => {
|
|
57
56
|
this.contrastTool.configContrastDragMode();
|
|
58
57
|
};
|
|
@@ -66,16 +65,19 @@ export class DrawToolCore extends CommToolsData {
|
|
|
66
65
|
this.contrastTool.repraintCurrentContrastSlice();
|
|
67
66
|
};
|
|
68
67
|
this.container = container;
|
|
69
|
-
this.mainAreaContainer = mainAreaContainer;
|
|
68
|
+
this.mainAreaContainer = state.protectedData.mainAreaContainer;
|
|
69
|
+
this.state = state;
|
|
70
|
+
this.renderer = new RenderingUtils(state);
|
|
70
71
|
this.initTools();
|
|
71
72
|
this.initDrawToolCore();
|
|
72
73
|
}
|
|
73
74
|
initTools() {
|
|
74
75
|
const toolCtx = {
|
|
75
|
-
nrrd_states: this.nrrd_states,
|
|
76
|
-
gui_states: this.gui_states,
|
|
77
|
-
protectedData: this.protectedData,
|
|
78
|
-
cursorPage: this.cursorPage,
|
|
76
|
+
nrrd_states: this.state.nrrd_states,
|
|
77
|
+
gui_states: this.state.gui_states,
|
|
78
|
+
protectedData: this.state.protectedData,
|
|
79
|
+
cursorPage: this.state.cursorPage,
|
|
80
|
+
callbacks: this.state.annotationCallbacks,
|
|
79
81
|
};
|
|
80
82
|
this.imageStoreHelper = new ImageStoreHelper(toolCtx, {
|
|
81
83
|
setEmptyCanvasSize: (axis) => this.setEmptyCanvasSize(axis),
|
|
@@ -84,7 +86,8 @@ export class DrawToolCore extends CommToolsData {
|
|
|
84
86
|
this.sphereTool = new SphereTool(toolCtx, {
|
|
85
87
|
setEmptyCanvasSize: (axis) => this.setEmptyCanvasSize(axis),
|
|
86
88
|
drawImageOnEmptyImage: (canvas) => this.drawImageOnEmptyImage(canvas),
|
|
87
|
-
|
|
89
|
+
enableCrosshair: () => this.enableCrosshair(),
|
|
90
|
+
setUpSphereOrigins: (x, y, s) => this.setUpSphereOrigins(x, y, s),
|
|
88
91
|
});
|
|
89
92
|
this.crosshairTool = new CrosshairTool(toolCtx);
|
|
90
93
|
this.contrastTool = new ContrastTool(toolCtx, this.container, this.contrastEventPrameters, {
|
|
@@ -97,12 +100,24 @@ export class DrawToolCore extends CommToolsData {
|
|
|
97
100
|
setIsDrawFalse: (target) => this.setIsDrawFalse(target),
|
|
98
101
|
});
|
|
99
102
|
this.eraserTool = new EraserTool(toolCtx);
|
|
103
|
+
this.panTool = new PanTool(toolCtx, {
|
|
104
|
+
zoomActionAfterDrawSphere: () => this.zoomActionAfterDrawSphere(),
|
|
105
|
+
});
|
|
106
|
+
this.drawingTool = new DrawingTool(toolCtx, {
|
|
107
|
+
setCurrentLayer: () => this.setCurrentLayer(),
|
|
108
|
+
compositeAllLayers: () => this.renderer.compositeAllLayers(),
|
|
109
|
+
syncLayerSliceData: (index, layer) => this.syncLayerSliceData(index, layer),
|
|
110
|
+
filterDrawedImage: (axis, index) => this.renderer.filterDrawedImage(axis, index),
|
|
111
|
+
getVolumeForLayer: (layer) => this.renderer.getVolumeForLayer(layer),
|
|
112
|
+
pushUndoDelta: (delta) => this.undoManager.push(delta),
|
|
113
|
+
getEraserUrls: () => this.eraserUrls,
|
|
114
|
+
});
|
|
100
115
|
}
|
|
101
116
|
initDrawToolCore() {
|
|
102
117
|
// Initialize EventRouter for centralized event handling
|
|
103
118
|
this.eventRouter = new EventRouter({
|
|
104
119
|
container: this.container,
|
|
105
|
-
canvas: this.protectedData.canvases.drawingCanvas,
|
|
120
|
+
canvas: this.state.protectedData.canvases.drawingCanvas,
|
|
106
121
|
onModeChange: (prevMode, newMode) => {
|
|
107
122
|
// Use string comparison to avoid TypeScript narrowing issues
|
|
108
123
|
const prev = prevMode;
|
|
@@ -112,77 +127,112 @@ export class DrawToolCore extends CommToolsData {
|
|
|
112
127
|
}
|
|
113
128
|
else if (prev === 'contrast') {
|
|
114
129
|
this.removeContrastDragMode();
|
|
115
|
-
this.gui_states.readyToUpdate = true;
|
|
130
|
+
this.state.gui_states.viewConfig.readyToUpdate = true;
|
|
116
131
|
}
|
|
117
132
|
if (next === 'crosshair') {
|
|
118
|
-
this.protectedData.
|
|
133
|
+
this.state.protectedData.isDrawing = false;
|
|
119
134
|
}
|
|
120
135
|
}
|
|
121
136
|
});
|
|
122
137
|
// Inject eventRouter into ToolContext so tools can query mode/state
|
|
123
138
|
const toolCtx = this.sphereTool['ctx'];
|
|
124
139
|
toolCtx.eventRouter = this.eventRouter;
|
|
125
|
-
// Configure keyboard settings from
|
|
126
|
-
this.eventRouter.setKeyboardSettings(this.
|
|
140
|
+
// Configure keyboard settings from state
|
|
141
|
+
this.eventRouter.setKeyboardSettings(this.state.keyboardSettings);
|
|
127
142
|
// Track undo flag for Ctrl+Z handling
|
|
128
143
|
let undoFlag = false;
|
|
129
144
|
// Register keyboard handlers with EventRouter
|
|
130
145
|
this.eventRouter.setKeydownHandler((ev) => {
|
|
131
|
-
|
|
132
|
-
if (this._configKeyBoard)
|
|
146
|
+
if (this.state.configKeyBoard)
|
|
133
147
|
return;
|
|
134
148
|
// Handle undo (Ctrl+Z)
|
|
135
|
-
if ((ev.ctrlKey || ev.metaKey) && ev.key === this.
|
|
149
|
+
if ((ev.ctrlKey || ev.metaKey) && ev.key === this.state.keyboardSettings.undo) {
|
|
136
150
|
undoFlag = true;
|
|
137
151
|
this.undoLastPainting();
|
|
138
152
|
}
|
|
139
153
|
// Handle redo (Ctrl+<redo key> or Ctrl+Shift+<undo key>)
|
|
140
|
-
const redoKey = this.
|
|
141
|
-
const undoKeyUpper = this.
|
|
154
|
+
const redoKey = this.state.keyboardSettings.redo;
|
|
155
|
+
const undoKeyUpper = this.state.keyboardSettings.undo.toUpperCase();
|
|
142
156
|
if ((ev.ctrlKey || ev.metaKey) && (ev.key === redoKey || (ev.shiftKey && ev.key === undoKeyUpper))) {
|
|
143
157
|
this.redoLastPainting();
|
|
144
158
|
}
|
|
145
|
-
// Handle crosshair toggle
|
|
146
|
-
if (ev.key === this.
|
|
147
|
-
|
|
148
|
-
(_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.toggleCrosshair();
|
|
149
|
-
}
|
|
159
|
+
// Handle crosshair toggle (allowed in drawing tools AND sphere mode)
|
|
160
|
+
if (ev.key === this.state.keyboardSettings.crosshair) {
|
|
161
|
+
this.eventRouter.toggleCrosshair();
|
|
150
162
|
}
|
|
151
163
|
// Handle draw mode (Shift key) - EventRouter already tracks this
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCtrlHeld()) {
|
|
164
|
+
if (ev.key === this.state.keyboardSettings.draw && !this.state.gui_states.mode.sphere) {
|
|
165
|
+
if (this.eventRouter.isCtrlHeld()) {
|
|
155
166
|
return; // Ctrl takes priority
|
|
156
167
|
}
|
|
157
|
-
|
|
168
|
+
}
|
|
169
|
+
// Handle sphere mode toggle
|
|
170
|
+
if (ev.key === this.state.keyboardSettings.sphere) {
|
|
171
|
+
// Block during draw mode or contrast mode
|
|
172
|
+
if (this.eventRouter.isShiftHeld() || this.eventRouter.isCtrlHeld()) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.state.gui_states.mode.sphere = !this.state.gui_states.mode.sphere;
|
|
176
|
+
if (this.state.gui_states.mode.sphere) {
|
|
177
|
+
this.enterSphereMode();
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this.exitSphereMode();
|
|
181
|
+
}
|
|
158
182
|
}
|
|
159
183
|
});
|
|
160
184
|
this.eventRouter.setKeyupHandler((ev) => {
|
|
161
|
-
|
|
162
|
-
if (this._configKeyBoard)
|
|
185
|
+
if (this.state.configKeyBoard)
|
|
163
186
|
return;
|
|
164
187
|
// Handle Ctrl key release (contrast mode toggle)
|
|
165
|
-
if (this.
|
|
188
|
+
if (this.state.keyboardSettings.contrast.includes(ev.key)) {
|
|
166
189
|
if (undoFlag) {
|
|
167
|
-
this.gui_states.readyToUpdate = true;
|
|
190
|
+
this.state.gui_states.viewConfig.readyToUpdate = true;
|
|
168
191
|
undoFlag = false;
|
|
169
192
|
return;
|
|
170
193
|
}
|
|
171
194
|
// Skip mode toggle when contrast shortcut is disabled
|
|
172
|
-
if (!
|
|
195
|
+
if (!this.eventRouter.isContrastEnabled())
|
|
196
|
+
return;
|
|
197
|
+
// Block contrast toggle during crosshair, draw, or sphere (mutual exclusion)
|
|
198
|
+
if (this.eventRouter.isCrosshairEnabled() || this.eventRouter.getMode() === 'draw')
|
|
173
199
|
return;
|
|
174
|
-
|
|
175
|
-
if (((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled()) || ((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.getMode()) === 'draw')
|
|
200
|
+
if (this.state.gui_states.mode.sphere)
|
|
176
201
|
return;
|
|
177
202
|
// Toggle contrast mode manually since it's on keyup
|
|
178
|
-
if (
|
|
179
|
-
|
|
203
|
+
if (this.eventRouter.getMode() !== 'contrast') {
|
|
204
|
+
this.eventRouter.setMode('contrast');
|
|
180
205
|
}
|
|
181
206
|
else {
|
|
182
|
-
|
|
207
|
+
this.eventRouter.setMode('idle');
|
|
183
208
|
}
|
|
184
209
|
}
|
|
185
210
|
});
|
|
211
|
+
// Register pointer handlers with EventRouter
|
|
212
|
+
this.eventRouter.setPointerMoveHandler((e) => {
|
|
213
|
+
if (this.drawingTool.isActive || this.panTool.isActive) {
|
|
214
|
+
this.drawingPrameters.handleOnDrawingMouseMove(e);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
this.eventRouter.setPointerUpHandler((e) => {
|
|
218
|
+
if (this.drawingTool.isActive || this.drawingTool.painting
|
|
219
|
+
|| this.panTool.isActive
|
|
220
|
+
|| (this.state.gui_states.mode.sphere && this.eventRouter.getMode() !== 'crosshair')) {
|
|
221
|
+
this.drawingPrameters.handleOnDrawingMouseUp(e);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
this.eventRouter.setPointerLeaveHandler((e) => {
|
|
225
|
+
this.handlePointerLeave();
|
|
226
|
+
});
|
|
227
|
+
// Register wheel handler with EventRouter
|
|
228
|
+
this.eventRouter.setWheelHandler((e) => {
|
|
229
|
+
if (this.activeWheelMode === 'zoom') {
|
|
230
|
+
this.drawingPrameters.handleMouseZoomSliceWheel(e);
|
|
231
|
+
}
|
|
232
|
+
else if (this.activeWheelMode === 'sphere') {
|
|
233
|
+
this.drawingPrameters.handleSphereWheel(e);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
186
236
|
// Bind all event listeners
|
|
187
237
|
this.eventRouter.bindAll();
|
|
188
238
|
}
|
|
@@ -191,544 +241,225 @@ export class DrawToolCore extends CommToolsData {
|
|
|
191
241
|
}
|
|
192
242
|
setPencilIconUrls(urls) {
|
|
193
243
|
this.pencilUrls = urls;
|
|
194
|
-
this.gui_states.defaultPaintCursor = switchPencilIcon("dot", this.pencilUrls);
|
|
195
|
-
this.protectedData.canvases.drawingCanvas.style.cursor =
|
|
196
|
-
this.gui_states.defaultPaintCursor;
|
|
244
|
+
this.state.gui_states.viewConfig.defaultPaintCursor = switchPencilIcon("dot", this.pencilUrls);
|
|
245
|
+
this.state.protectedData.canvases.drawingCanvas.style.cursor =
|
|
246
|
+
this.state.gui_states.viewConfig.defaultPaintCursor;
|
|
197
247
|
}
|
|
198
248
|
setCurrentLayer() {
|
|
199
|
-
const layer = this.gui_states.layer;
|
|
200
|
-
let target = this.protectedData.layerTargets.get(layer);
|
|
249
|
+
const layer = this.state.gui_states.layerChannel.layer;
|
|
250
|
+
let target = this.state.protectedData.layerTargets.get(layer);
|
|
201
251
|
if (!target) {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
target = this.protectedData.layerTargets.get(firstId);
|
|
252
|
+
const firstId = this.state.nrrd_states.image.layers[0];
|
|
253
|
+
target = this.state.protectedData.layerTargets.get(firstId);
|
|
205
254
|
}
|
|
206
255
|
return { ctx: target.ctx, canvas: target.canvas };
|
|
207
256
|
}
|
|
208
257
|
draw(opts) {
|
|
209
258
|
if (!!opts) {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
259
|
+
if (opts.getMaskData) {
|
|
260
|
+
this.state.annotationCallbacks.onMaskChanged = opts.getMaskData;
|
|
261
|
+
}
|
|
262
|
+
if (opts.onClearLayerVolume) {
|
|
263
|
+
this.state.annotationCallbacks.onLayerVolumeCleared = opts.onClearLayerVolume;
|
|
264
|
+
}
|
|
265
|
+
if (opts.getSphereData) {
|
|
266
|
+
this.state.annotationCallbacks.onSphereChanged = opts.getSphereData;
|
|
267
|
+
}
|
|
268
|
+
if (opts.getCalculateSpherePositionsData) {
|
|
269
|
+
this.state.annotationCallbacks.onCalculatorPositionsChanged = opts.getCalculateSpherePositionsData;
|
|
213
270
|
}
|
|
214
|
-
this.nrrd_states.getSphere = opts === null || opts === void 0 ? void 0 : opts.getSphereData;
|
|
215
|
-
this.nrrd_states.getCalculateSpherePositions = opts === null || opts === void 0 ? void 0 : opts.getCalculateSpherePositionsData;
|
|
216
271
|
}
|
|
217
272
|
this.paintOnCanvas();
|
|
218
273
|
}
|
|
219
|
-
drawCalSphereDown(x, y, sliceIndex, cal_position) {
|
|
220
|
-
this.nrrd_states.sphereRadius = 5;
|
|
221
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
222
|
-
let mouseX = x / this.nrrd_states.sizeFoctor;
|
|
223
|
-
let mouseY = y / this.nrrd_states.sizeFoctor;
|
|
224
|
-
// record mouseX,Y, and enable crosshair function
|
|
225
|
-
this.nrrd_states.sphereOrigin[this.protectedData.axis] = [
|
|
226
|
-
mouseX,
|
|
227
|
-
mouseY,
|
|
228
|
-
sliceIndex,
|
|
229
|
-
];
|
|
230
|
-
this.setUpSphereOrigins(mouseX, mouseY, sliceIndex);
|
|
231
|
-
// Note the sphere origin here is x, y, z
|
|
232
|
-
// x: pixel x, y: pixel y, z: slice index (mm)
|
|
233
|
-
switch (cal_position) {
|
|
234
|
-
case "tumour":
|
|
235
|
-
this.nrrd_states.tumourSphereOrigin = JSON.parse(JSON.stringify(this.nrrd_states.sphereOrigin));
|
|
236
|
-
break;
|
|
237
|
-
case "skin":
|
|
238
|
-
this.nrrd_states.skinSphereOrigin = JSON.parse(JSON.stringify(this.nrrd_states.sphereOrigin));
|
|
239
|
-
break;
|
|
240
|
-
case "nipple":
|
|
241
|
-
this.nrrd_states.nippleSphereOrigin = JSON.parse(JSON.stringify(this.nrrd_states.sphereOrigin));
|
|
242
|
-
break;
|
|
243
|
-
case "ribcage":
|
|
244
|
-
this.nrrd_states.ribSphereOrigin = JSON.parse(JSON.stringify(this.nrrd_states.sphereOrigin));
|
|
245
|
-
break;
|
|
246
|
-
}
|
|
247
|
-
this.nrrd_states.cursorPageX = mouseX;
|
|
248
|
-
this.nrrd_states.cursorPageY = mouseY;
|
|
249
|
-
this.enableCrosshair();
|
|
250
|
-
// draw circle setup width/height for sphere canvas
|
|
251
|
-
this.drawCalculatorSphere(this.nrrd_states.sphereRadius);
|
|
252
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
253
|
-
}
|
|
254
|
-
drawCalSphereUp() {
|
|
255
|
-
// TODO send data to outside
|
|
256
|
-
// this.clearStoreImages();
|
|
257
|
-
this.clearSpherePrintStoreImages();
|
|
258
|
-
this.drawCalculatorSphereOnEachViews("x");
|
|
259
|
-
this.drawCalculatorSphereOnEachViews("y");
|
|
260
|
-
this.drawCalculatorSphereOnEachViews("z");
|
|
261
|
-
!!this.nrrd_states.getCalculateSpherePositions &&
|
|
262
|
-
this.nrrd_states.getCalculateSpherePositions(this.nrrd_states.tumourSphereOrigin, this.nrrd_states.skinSphereOrigin, this.nrrd_states.ribSphereOrigin, this.nrrd_states.nippleSphereOrigin, this.protectedData.axis);
|
|
263
|
-
this.zoomActionAfterDrawSphere();
|
|
264
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
265
|
-
}
|
|
266
274
|
zoomActionAfterDrawSphere() {
|
|
267
|
-
this.
|
|
268
|
-
}
|
|
269
|
-
clearSpherePrintStoreImages() {
|
|
270
|
-
this.sphereTool.clearSpherePrintStoreImages();
|
|
275
|
+
this.activeWheelMode = 'zoom';
|
|
271
276
|
}
|
|
272
277
|
paintOnCanvas() {
|
|
273
278
|
var _a, _b, _c;
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
let rightclicked = false;
|
|
279
|
-
let panelMoveInnerX = 0;
|
|
280
|
-
let panelMoveInnerY = 0;
|
|
281
|
-
// todo
|
|
282
|
-
// let currentSliceIndex = this.protectedData.mainPreSlices.index;
|
|
283
|
-
let currentSliceIndex = this.protectedData.mainPreSlices.index;
|
|
284
|
-
// draw lines starts position
|
|
285
|
-
let Is_Painting = false;
|
|
286
|
-
let lines = [];
|
|
287
|
-
const clearArc = this.useEraser();
|
|
279
|
+
// Initialize tools for this paint cycle
|
|
280
|
+
this.drawingTool.reset(this.useEraser());
|
|
281
|
+
this.panTool.reset();
|
|
282
|
+
this.paintSliceIndex = this.state.protectedData.mainPreSlices.index;
|
|
288
283
|
this.updateOriginAndChangedWH();
|
|
289
284
|
this.initAllCanvas();
|
|
290
|
-
(_a = this.protectedData.ctxes.displayCtx) === null || _a === void 0 ? void 0 : _a.save();
|
|
285
|
+
(_a = this.state.protectedData.ctxes.displayCtx) === null || _a === void 0 ? void 0 : _a.save();
|
|
291
286
|
this.flipDisplayImageByAxis();
|
|
292
|
-
(_b = this.protectedData.ctxes.displayCtx) === null || _b === void 0 ? void 0 : _b.drawImage(this.protectedData.canvases.originCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
293
|
-
(_c = this.protectedData.ctxes.displayCtx) === null || _c === void 0 ? void 0 : _c.restore();
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// let a global variable to store the wheel move event
|
|
297
|
-
// Remove existing listener before creating a new one to prevent leaks
|
|
298
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
299
|
-
if (this._keyboardSettings.mouseWheel === "Scroll:Zoom") {
|
|
287
|
+
(_b = this.state.protectedData.ctxes.displayCtx) === null || _b === void 0 ? void 0 : _b.drawImage(this.state.protectedData.canvases.originCanvas, 0, 0, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
288
|
+
(_c = this.state.protectedData.ctxes.displayCtx) === null || _c === void 0 ? void 0 : _c.restore();
|
|
289
|
+
// Configure wheel handler functions
|
|
290
|
+
if (this.state.keyboardSettings.mouseWheel === "Scroll:Zoom") {
|
|
300
291
|
this.drawingPrameters.handleMouseZoomSliceWheel = this.configMouseZoomWheel();
|
|
301
292
|
}
|
|
302
293
|
else {
|
|
303
294
|
this.drawingPrameters.handleMouseZoomSliceWheel = this.configMouseSliceWheel();
|
|
304
295
|
}
|
|
305
|
-
// Keep wheel as direct addEventListener due to dynamic add/remove patterns in handleOnDrawingMouseUp
|
|
306
|
-
// EventRouter routing would conflict with the dynamic wheel switching (zoom vs sphere wheel)
|
|
307
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel, {
|
|
308
|
-
passive: false,
|
|
309
|
-
});
|
|
310
296
|
// sphere Wheel
|
|
311
297
|
this.drawingPrameters.handleSphereWheel = this.configMouseSphereWheel();
|
|
312
|
-
//
|
|
313
|
-
this.drawingPrameters.
|
|
314
|
-
this.
|
|
315
|
-
|
|
316
|
-
this.nrrd_states.previousPanelT = e.clientY - panelMoveInnerY;
|
|
317
|
-
this.protectedData.canvases.displayCanvas.style.left =
|
|
318
|
-
this.protectedData.canvases.drawingCanvas.style.left =
|
|
319
|
-
this.nrrd_states.previousPanelL + "px";
|
|
320
|
-
this.protectedData.canvases.displayCanvas.style.top =
|
|
321
|
-
this.protectedData.canvases.drawingCanvas.style.top =
|
|
322
|
-
this.nrrd_states.previousPanelT + "px";
|
|
323
|
-
};
|
|
324
|
-
// brush circle move
|
|
325
|
-
this.drawingPrameters.handleOnDrawingBrushCricleMove = (e) => {
|
|
326
|
-
e.preventDefault();
|
|
327
|
-
this.nrrd_states.Mouse_Over_x = e.offsetX;
|
|
328
|
-
this.nrrd_states.Mouse_Over_y = e.offsetY;
|
|
329
|
-
if (this.nrrd_states.Mouse_Over_x === undefined) {
|
|
330
|
-
this.nrrd_states.Mouse_Over_x = e.clientX;
|
|
331
|
-
this.nrrd_states.Mouse_Over_y = e.clientY;
|
|
332
|
-
}
|
|
333
|
-
if (e.type === "mouseout") {
|
|
334
|
-
this.nrrd_states.Mouse_Over = false;
|
|
335
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("mousemove", this.drawingPrameters.handleOnDrawingBrushCricleMove);
|
|
336
|
-
}
|
|
337
|
-
else if (e.type === "mouseover") {
|
|
338
|
-
this.nrrd_states.Mouse_Over = true;
|
|
339
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("mousemove", this.drawingPrameters.handleOnDrawingBrushCricleMove);
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
// drawing move
|
|
298
|
+
// brush circle move — delegated to DrawingTool
|
|
299
|
+
this.drawingPrameters.handleOnDrawingBrushCricleMove =
|
|
300
|
+
this.drawingTool.createBrushTrackingHandler();
|
|
301
|
+
// drawing move — delegated to DrawingTool
|
|
343
302
|
this.drawingPrameters.handleOnDrawingMouseMove = (e) => {
|
|
344
|
-
this.
|
|
345
|
-
if (Is_Painting) {
|
|
346
|
-
if (this.gui_states.Eraser) {
|
|
347
|
-
this.nrrd_states.stepClear = 1;
|
|
348
|
-
// drawingCtx.clearRect(e.offsetX - 5, e.offsetY - 5, 25, 25);
|
|
349
|
-
clearArc(e.offsetX, e.offsetY, this.gui_states.brushAndEraserSize);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
lines.push({ x: e.offsetX, y: e.offsetY });
|
|
353
|
-
this.paintOnCanvasLayer(e.offsetX, e.offsetY);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
303
|
+
this.drawingTool.onPointerMove(e);
|
|
356
304
|
};
|
|
357
305
|
this.drawingPrameters.handleOnDrawingMouseDown = (e) => {
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
361
|
-
this.protectedData.ctxes.drawingLayerMasterCtx.closePath();
|
|
306
|
+
if (this.drawingTool.isActive || this.panTool.isActive) {
|
|
307
|
+
this.state.protectedData.ctxes.drawingLayerMasterCtx.closePath();
|
|
362
308
|
return;
|
|
363
309
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
310
|
+
if (this.paintSliceIndex !== this.state.protectedData.mainPreSlices.index) {
|
|
311
|
+
this.paintSliceIndex = this.state.protectedData.mainPreSlices.index;
|
|
312
|
+
}
|
|
313
|
+
// Suppress wheel only when starting a draw operation
|
|
314
|
+
if (this.eventRouter.getMode() === 'draw') {
|
|
315
|
+
this.activeWheelMode = 'none';
|
|
370
316
|
}
|
|
371
|
-
// remove it when mouse click down
|
|
372
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
373
317
|
if (e.button === 0) {
|
|
374
|
-
if (
|
|
375
|
-
|
|
376
|
-
lines = [];
|
|
377
|
-
Is_Painting = true;
|
|
378
|
-
this.protectedData.Is_Draw = true;
|
|
379
|
-
if (this.gui_states.Eraser) {
|
|
380
|
-
// this.protectedData.canvases.drawingCanvas.style.cursor =
|
|
381
|
-
// "url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_48.png) 48 48, crosshair";
|
|
382
|
-
this.eraserUrls.length > 0
|
|
383
|
-
? (this.protectedData.canvases.drawingCanvas.style.cursor =
|
|
384
|
-
switchEraserSize(this.gui_states.brushAndEraserSize, this.eraserUrls))
|
|
385
|
-
: (this.protectedData.canvases.drawingCanvas.style.cursor =
|
|
386
|
-
switchEraserSize(this.gui_states.brushAndEraserSize));
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
this.protectedData.canvases.drawingCanvas.style.cursor =
|
|
390
|
-
this.gui_states.defaultPaintCursor;
|
|
391
|
-
}
|
|
392
|
-
this.nrrd_states.drawStartPos.x = e.offsetX;
|
|
393
|
-
this.nrrd_states.drawStartPos.y = e.offsetY;
|
|
394
|
-
// Capture pre-draw slice snapshot for undo
|
|
395
|
-
try {
|
|
396
|
-
this.preDrawAxis = this.protectedData.axis;
|
|
397
|
-
this.preDrawSliceIndex = this.nrrd_states.currentIndex;
|
|
398
|
-
const vol = this.getVolumeForLayer(this.gui_states.layer);
|
|
399
|
-
this.preDrawSlice = vol.getSliceUint8(this.preDrawSliceIndex, this.preDrawAxis).data.slice();
|
|
400
|
-
}
|
|
401
|
-
catch (_e) {
|
|
402
|
-
this.preDrawSlice = null;
|
|
403
|
-
}
|
|
404
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
405
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointermove", this.drawingPrameters.handleOnDrawingMouseMove);
|
|
318
|
+
if (this.eventRouter.getMode() === 'draw') {
|
|
319
|
+
this.drawingTool.onPointerDown(e);
|
|
406
320
|
}
|
|
407
|
-
else if (
|
|
408
|
-
this.nrrd_states.cursorPageX =
|
|
409
|
-
e.offsetX / this.nrrd_states.
|
|
410
|
-
this.nrrd_states.cursorPageY =
|
|
411
|
-
e.offsetY / this.nrrd_states.
|
|
321
|
+
else if (this.eventRouter.isCrosshairEnabled()) {
|
|
322
|
+
this.state.nrrd_states.interaction.cursorPageX =
|
|
323
|
+
e.offsetX / this.state.nrrd_states.view.sizeFactor;
|
|
324
|
+
this.state.nrrd_states.interaction.cursorPageY =
|
|
325
|
+
e.offsetY / this.state.nrrd_states.view.sizeFactor;
|
|
412
326
|
this.enableCrosshair();
|
|
413
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
414
327
|
}
|
|
415
|
-
else if (this.gui_states.sphere && !
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
else if (this.gui_states.calculator && !((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.isCrosshairEnabled())) {
|
|
419
|
-
this.drawCalSphereDown(e.offsetX, e.offsetY, this.nrrd_states.currentIndex, this.gui_states.cal_distance);
|
|
328
|
+
else if (this.state.gui_states.mode.sphere && !this.eventRouter.isCrosshairEnabled()) {
|
|
329
|
+
this.handleSphereClick(e);
|
|
420
330
|
}
|
|
421
331
|
}
|
|
422
332
|
else if (e.button === 2) {
|
|
423
|
-
|
|
424
|
-
let offsetX = this.protectedData.canvases.drawingCanvas.offsetLeft;
|
|
425
|
-
let offsetY = this.protectedData.canvases.drawingCanvas.offsetTop;
|
|
426
|
-
panelMoveInnerX = e.clientX - offsetX;
|
|
427
|
-
panelMoveInnerY = e.clientY - offsetY;
|
|
428
|
-
this.protectedData.canvases.drawingCanvas.style.cursor = "grab";
|
|
429
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
430
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointermove", this.drawingPrameters.handleOnPanMouseMove);
|
|
333
|
+
this.panTool.onPointerDown(e);
|
|
431
334
|
}
|
|
432
335
|
else {
|
|
433
336
|
return;
|
|
434
337
|
}
|
|
435
338
|
};
|
|
436
|
-
// Route pointerdown through EventRouter
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
this.drawingPrameters.handleOnDrawingMouseDown(e);
|
|
441
|
-
});
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
// Fallback for legacy mode without EventRouter
|
|
445
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerdown", this.drawingPrameters.handleOnDrawingMouseDown, true);
|
|
446
|
-
}
|
|
447
|
-
const sphere = (e) => {
|
|
448
|
-
// set sphere size
|
|
449
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
450
|
-
let mouseX = e.offsetX / this.nrrd_states.sizeFoctor;
|
|
451
|
-
let mouseY = e.offsetY / this.nrrd_states.sizeFoctor;
|
|
452
|
-
// record mouseX,Y, and enable crosshair function
|
|
453
|
-
this.nrrd_states.sphereOrigin[this.protectedData.axis] = [
|
|
454
|
-
mouseX,
|
|
455
|
-
mouseY,
|
|
456
|
-
this.nrrd_states.currentIndex,
|
|
457
|
-
];
|
|
458
|
-
this.setUpSphereOrigins(mouseX, mouseY, this.nrrd_states.currentIndex);
|
|
459
|
-
this.nrrd_states.cursorPageX = mouseX;
|
|
460
|
-
this.nrrd_states.cursorPageY = mouseY;
|
|
461
|
-
this.enableCrosshair();
|
|
462
|
-
// draw circle setup width/height for sphere canvas
|
|
463
|
-
this.drawSphere(mouseX, mouseY, this.nrrd_states.sphereRadius);
|
|
464
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleSphereWheel, true);
|
|
465
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
466
|
-
};
|
|
467
|
-
const redrawPreviousImageToLayerCtx = (ctx) => {
|
|
468
|
-
var _a;
|
|
469
|
-
const tempPreImg = (_a = this.filterDrawedImage(this.protectedData.axis, this.nrrd_states.currentIndex)) === null || _a === void 0 ? void 0 : _a.image;
|
|
470
|
-
this.protectedData.canvases.emptyCanvas.width =
|
|
471
|
-
this.protectedData.canvases.emptyCanvas.width;
|
|
472
|
-
if (tempPreImg) {
|
|
473
|
-
this.protectedData.previousDrawingImage = tempPreImg;
|
|
474
|
-
}
|
|
475
|
-
this.protectedData.ctxes.emptyCtx.putImageData(tempPreImg, 0, 0);
|
|
476
|
-
// No flip needed: MaskVolume stores in source coordinates (matching the
|
|
477
|
-
// Three.js / layer canvas convention). The layer canvas is in screen
|
|
478
|
-
// coordinates, which already match the source coordinate system.
|
|
479
|
-
ctx.imageSmoothingEnabled = false;
|
|
480
|
-
ctx.drawImage(this.protectedData.canvases.emptyCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
481
|
-
};
|
|
339
|
+
// Route pointerdown through EventRouter
|
|
340
|
+
this.eventRouter.setPointerDownHandler((e) => {
|
|
341
|
+
this.drawingPrameters.handleOnDrawingMouseDown(e);
|
|
342
|
+
});
|
|
482
343
|
this.drawingPrameters.handleOnDrawingMouseUp = (e) => {
|
|
483
|
-
var _a, _b, _c, _d;
|
|
484
344
|
if (e.button === 0) {
|
|
485
|
-
if (
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
ctx.closePath();
|
|
489
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointermove", this.drawingPrameters.handleOnDrawingMouseMove);
|
|
490
|
-
if (!this.gui_states.Eraser) {
|
|
491
|
-
if (this.gui_states.pencil) {
|
|
492
|
-
// Clear only the current layer canvas (NOT master)
|
|
493
|
-
canvas.width = canvas.width;
|
|
494
|
-
// Redraw previous layer data from volume
|
|
495
|
-
redrawPreviousImageToLayerCtx(ctx);
|
|
496
|
-
// Draw new pencil strokes on current layer canvas
|
|
497
|
-
ctx.beginPath();
|
|
498
|
-
ctx.moveTo(lines[0].x, lines[0].y);
|
|
499
|
-
for (let i = 1; i < lines.length; i++) {
|
|
500
|
-
ctx.lineTo(lines[i].x, lines[i].y);
|
|
501
|
-
}
|
|
502
|
-
ctx.closePath();
|
|
503
|
-
ctx.lineWidth = 1;
|
|
504
|
-
ctx.fillStyle = this.gui_states.fillColor;
|
|
505
|
-
ctx.fill();
|
|
506
|
-
// Composite ALL layers to master (not just current layer)
|
|
507
|
-
this.compositeAllLayers();
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
this.protectedData.previousDrawingImage =
|
|
511
|
-
this.protectedData.ctxes.drawingLayerMasterCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvasLayerMaster.width, this.protectedData.canvases.drawingCanvasLayerMaster.height);
|
|
512
|
-
this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.layer);
|
|
513
|
-
if (this.gui_states.Eraser) {
|
|
514
|
-
const restLayers = this.getRestLayer();
|
|
515
|
-
this.storeEachLayerImage(this.nrrd_states.currentIndex, restLayers[0]);
|
|
516
|
-
this.storeEachLayerImage(this.nrrd_states.currentIndex, restLayers[1]);
|
|
517
|
-
}
|
|
518
|
-
Is_Painting = false;
|
|
519
|
-
// Push delta to UndoManager (new Delta-based undo system)
|
|
520
|
-
if (this.preDrawSlice) {
|
|
521
|
-
try {
|
|
522
|
-
const vol = this.getVolumeForLayer(this.gui_states.layer);
|
|
523
|
-
const { data: newSlice } = vol.getSliceUint8(this.preDrawSliceIndex, this.preDrawAxis);
|
|
524
|
-
const delta = {
|
|
525
|
-
layerId: this.gui_states.layer,
|
|
526
|
-
axis: this.preDrawAxis,
|
|
527
|
-
sliceIndex: this.preDrawSliceIndex,
|
|
528
|
-
oldSlice: this.preDrawSlice,
|
|
529
|
-
newSlice: newSlice.slice(),
|
|
530
|
-
};
|
|
531
|
-
this.undoManager.push(delta);
|
|
532
|
-
}
|
|
533
|
-
catch (_e) {
|
|
534
|
-
// Volume not ready — skip
|
|
535
|
-
}
|
|
536
|
-
this.preDrawSlice = null;
|
|
537
|
-
}
|
|
538
|
-
// add wheel after pointer up
|
|
539
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel, {
|
|
540
|
-
passive: false,
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
else if (this.gui_states.sphere &&
|
|
544
|
-
!((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled())) {
|
|
545
|
-
// plan B
|
|
546
|
-
// findout all index in the sphere radius range in Axial view
|
|
547
|
-
if (this.nrrd_states.spherePlanB) {
|
|
548
|
-
// clear stroe images
|
|
549
|
-
// this.clearStoreImages();
|
|
550
|
-
this.clearSpherePrintStoreImages();
|
|
551
|
-
for (let i = 0; i < this.nrrd_states.sphereRadius; i++) {
|
|
552
|
-
this.drawSphereOnEachViews(i, "x");
|
|
553
|
-
this.drawSphereOnEachViews(i, "y");
|
|
554
|
-
this.drawSphereOnEachViews(i, "z");
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
!!this.nrrd_states.getSphere &&
|
|
558
|
-
this.nrrd_states.getSphere(this.nrrd_states.sphereOrigin.z, this.nrrd_states.sphereRadius / this.nrrd_states.sizeFoctor);
|
|
559
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleSphereWheel, true);
|
|
560
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
561
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
345
|
+
if (this.eventRouter.getMode() === 'draw' || this.drawingTool.painting) {
|
|
346
|
+
this.drawingTool.onPointerUp(e);
|
|
347
|
+
this.activeWheelMode = 'zoom';
|
|
562
348
|
}
|
|
563
|
-
else if (
|
|
564
|
-
|
|
565
|
-
this.
|
|
566
|
-
this.
|
|
349
|
+
else if (this.state.gui_states.mode.sphere &&
|
|
350
|
+
!this.eventRouter.isCrosshairEnabled()) {
|
|
351
|
+
this.sphereTool.onSpherePointerUp();
|
|
352
|
+
this.activeWheelMode = 'zoom';
|
|
353
|
+
this.zoomActionAfterDrawSphere();
|
|
567
354
|
}
|
|
568
|
-
else if (this.gui_states.
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
this.drawCalSphereUp();
|
|
355
|
+
else if (this.state.gui_states.mode.sphere &&
|
|
356
|
+
this.eventRouter.isCrosshairEnabled()) {
|
|
357
|
+
this.activeWheelMode = 'zoom';
|
|
572
358
|
}
|
|
573
359
|
}
|
|
574
360
|
else if (e.button === 2) {
|
|
575
|
-
|
|
576
|
-
this.protectedData.canvases.drawingCanvas.style.cursor = "grab";
|
|
577
|
-
setTimeout(() => {
|
|
578
|
-
this.protectedData.canvases.drawingCanvas.style.cursor = this.gui_states.defaultPaintCursor;
|
|
579
|
-
}, 2000);
|
|
580
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointermove", this.drawingPrameters.handleOnPanMouseMove);
|
|
581
|
-
if (this.gui_states.sphere || this.gui_states.calculator) {
|
|
582
|
-
this.zoomActionAfterDrawSphere();
|
|
583
|
-
}
|
|
361
|
+
this.panTool.onPointerUp(e, this.state.gui_states.viewConfig.defaultPaintCursor);
|
|
584
362
|
}
|
|
585
363
|
else {
|
|
586
364
|
return;
|
|
587
365
|
}
|
|
588
|
-
if (!this.gui_states.pencil) {
|
|
366
|
+
if (!this.state.gui_states.mode.pencil) {
|
|
589
367
|
this.setIsDrawFalse(100);
|
|
590
368
|
}
|
|
591
369
|
};
|
|
592
|
-
this.protectedData.canvases.drawingCanvas.addEventListener("pointerleave", (e) => {
|
|
593
|
-
Is_Painting = false;
|
|
594
|
-
if (leftclicked) {
|
|
595
|
-
leftclicked = false;
|
|
596
|
-
this.protectedData.ctxes.drawingLayerMasterCtx.closePath();
|
|
597
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointermove", this.drawingPrameters.handleOnDrawingMouseMove);
|
|
598
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleSphereWheel, true);
|
|
599
|
-
}
|
|
600
|
-
if (rightclicked) {
|
|
601
|
-
rightclicked = false;
|
|
602
|
-
this.protectedData.canvases.drawingCanvas.style.cursor = "grab";
|
|
603
|
-
this.protectedData.canvases.drawingCanvas.removeEventListener("pointermove", this.drawingPrameters.handleOnPanMouseMove);
|
|
604
|
-
}
|
|
605
|
-
this.setIsDrawFalse(100);
|
|
606
|
-
if (this.gui_states.pencil) {
|
|
607
|
-
this.setIsDrawFalse(1000);
|
|
608
|
-
}
|
|
609
|
-
});
|
|
610
370
|
this.start = () => {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
this.protectedData.ctxes.drawingCtx.
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
this.protectedData.ctxes.drawingLayerMasterCtx.
|
|
618
|
-
this.protectedData.
|
|
619
|
-
for (const [, target] of this.protectedData.layerTargets) {
|
|
371
|
+
if (this.state.gui_states.viewConfig.readyToUpdate) {
|
|
372
|
+
this.state.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
373
|
+
this.state.protectedData.ctxes.drawingCtx.globalAlpha =
|
|
374
|
+
this.state.gui_states.drawing.globalAlpha;
|
|
375
|
+
if (this.state.protectedData.isDrawing) {
|
|
376
|
+
this.state.protectedData.ctxes.drawingLayerMasterCtx.lineCap = "round";
|
|
377
|
+
this.state.protectedData.ctxes.drawingLayerMasterCtx.globalAlpha = 1;
|
|
378
|
+
for (const [, target] of this.state.protectedData.layerTargets) {
|
|
620
379
|
target.ctx.lineCap = "round";
|
|
621
380
|
target.ctx.globalAlpha = 1;
|
|
622
381
|
}
|
|
623
382
|
}
|
|
624
383
|
else {
|
|
625
|
-
|
|
626
|
-
const currentMode = (_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode();
|
|
384
|
+
const currentMode = this.eventRouter.getMode();
|
|
627
385
|
if (currentMode === 'draw') {
|
|
628
|
-
|
|
629
|
-
if (!this.gui_states.pencil &&
|
|
630
|
-
!this.gui_states.Eraser &&
|
|
631
|
-
this.nrrd_states.Mouse_Over) {
|
|
632
|
-
this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
633
|
-
this.protectedData.ctxes.drawingCtx.fillStyle =
|
|
634
|
-
this.gui_states.brushColor;
|
|
635
|
-
this.protectedData.ctxes.drawingCtx.beginPath();
|
|
636
|
-
this.protectedData.ctxes.drawingCtx.arc(this.nrrd_states.Mouse_Over_x, this.nrrd_states.Mouse_Over_y, this.gui_states.brushAndEraserSize / 2 + 1, 0, Math.PI * 2);
|
|
637
|
-
this.protectedData.ctxes.drawingCtx.strokeStyle =
|
|
638
|
-
this.gui_states.brushColor;
|
|
639
|
-
this.protectedData.ctxes.drawingCtx.stroke();
|
|
640
|
-
}
|
|
386
|
+
this.drawingTool.renderBrushPreview(this.state.protectedData.ctxes.drawingCtx, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
641
387
|
}
|
|
642
|
-
else if (currentMode === 'crosshair' ||
|
|
643
|
-
|
|
644
|
-
this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
645
|
-
const ex = this.nrrd_states.cursorPageX * this.nrrd_states.sizeFoctor;
|
|
646
|
-
const ey = this.nrrd_states.cursorPageY * this.nrrd_states.sizeFoctor;
|
|
647
|
-
this.drawLine(ex, 0, ex, this.nrrd_states.changedWidth);
|
|
648
|
-
this.drawLine(0, ey, this.nrrd_states.changedHeight, ey);
|
|
388
|
+
else if (currentMode === 'crosshair' || this.eventRouter.isCrosshairEnabled()) {
|
|
389
|
+
this.crosshairTool.renderCrosshair(this.state.protectedData.ctxes.drawingCtx, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
649
390
|
}
|
|
650
391
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
392
|
+
this.state.protectedData.ctxes.drawingCtx.drawImage(this.state.protectedData.canvases.drawingCanvasLayerMaster, 0, 0);
|
|
393
|
+
if (this.state.gui_states.mode.sphere) {
|
|
394
|
+
this.state.protectedData.ctxes.drawingCtx.drawImage(this.state.protectedData.canvases.drawingSphereCanvas, 0, 0, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
395
|
+
}
|
|
654
396
|
}
|
|
655
397
|
else {
|
|
656
398
|
this.redrawDisplayCanvas();
|
|
657
399
|
}
|
|
658
400
|
};
|
|
659
401
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
402
|
+
/**
|
|
403
|
+
* Extracted from paintOnCanvas() pointerleave handler.
|
|
404
|
+
*/
|
|
405
|
+
handlePointerLeave() {
|
|
406
|
+
const wasDrawing = this.drawingTool.onPointerLeave();
|
|
407
|
+
if (wasDrawing) {
|
|
408
|
+
this.activeWheelMode = 'zoom';
|
|
666
409
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
410
|
+
this.panTool.onPointerLeave();
|
|
411
|
+
this.setIsDrawFalse(100);
|
|
412
|
+
if (this.state.gui_states.mode.pencil) {
|
|
413
|
+
this.setIsDrawFalse(1000);
|
|
670
414
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
let { ctx, canvas } = this.setCurrentLayer();
|
|
677
|
-
// Draw only on the current layer canvas (not master directly)
|
|
678
|
-
this.drawLinesOnLayer(ctx, x, y);
|
|
679
|
-
// Composite all layers to master to preserve other layers' data
|
|
680
|
-
this.compositeAllLayers();
|
|
681
|
-
// reset drawing start position to current position.
|
|
682
|
-
this.nrrd_states.drawStartPos.x = x;
|
|
683
|
-
this.nrrd_states.drawStartPos.y = y;
|
|
684
|
-
// need to flag the map as needing updating.
|
|
685
|
-
this.protectedData.mainPreSlices.mesh.material.map.needsUpdate = true;
|
|
415
|
+
}
|
|
416
|
+
/** Extracted from paintOnCanvas() — handles sphere placement on left-click */
|
|
417
|
+
handleSphereClick(e) {
|
|
418
|
+
this.activeWheelMode = 'sphere';
|
|
419
|
+
this.sphereTool.onSphereClick(e);
|
|
686
420
|
}
|
|
687
421
|
initAllCanvas() {
|
|
688
422
|
/**
|
|
689
423
|
* display canvas
|
|
690
424
|
*/
|
|
691
|
-
this.protectedData.canvases.displayCanvas.style.position = "absolute";
|
|
692
|
-
this.protectedData.canvases.displayCanvas.style.zIndex = "9";
|
|
693
|
-
this.protectedData.canvases.displayCanvas.width =
|
|
694
|
-
this.nrrd_states.changedWidth;
|
|
695
|
-
this.protectedData.canvases.displayCanvas.height =
|
|
696
|
-
this.nrrd_states.changedHeight;
|
|
425
|
+
this.state.protectedData.canvases.displayCanvas.style.position = "absolute";
|
|
426
|
+
this.state.protectedData.canvases.displayCanvas.style.zIndex = "9";
|
|
427
|
+
this.state.protectedData.canvases.displayCanvas.width =
|
|
428
|
+
this.state.nrrd_states.view.changedWidth;
|
|
429
|
+
this.state.protectedData.canvases.displayCanvas.height =
|
|
430
|
+
this.state.nrrd_states.view.changedHeight;
|
|
697
431
|
/**
|
|
698
432
|
* drawing canvas
|
|
699
433
|
*/
|
|
700
|
-
this.protectedData.canvases.drawingCanvas.style.zIndex = "10";
|
|
701
|
-
this.protectedData.canvases.drawingCanvas.style.position = "absolute";
|
|
702
|
-
this.protectedData.canvases.drawingCanvas.width =
|
|
703
|
-
this.nrrd_states.changedWidth;
|
|
704
|
-
this.protectedData.canvases.drawingCanvas.height =
|
|
705
|
-
this.nrrd_states.changedHeight;
|
|
706
|
-
this.protectedData.canvases.drawingCanvas.style.cursor =
|
|
707
|
-
this.gui_states.defaultPaintCursor;
|
|
708
|
-
this.protectedData.canvases.drawingCanvas.oncontextmenu = () => false;
|
|
434
|
+
this.state.protectedData.canvases.drawingCanvas.style.zIndex = "10";
|
|
435
|
+
this.state.protectedData.canvases.drawingCanvas.style.position = "absolute";
|
|
436
|
+
this.state.protectedData.canvases.drawingCanvas.width =
|
|
437
|
+
this.state.nrrd_states.view.changedWidth;
|
|
438
|
+
this.state.protectedData.canvases.drawingCanvas.height =
|
|
439
|
+
this.state.nrrd_states.view.changedHeight;
|
|
440
|
+
this.state.protectedData.canvases.drawingCanvas.style.cursor =
|
|
441
|
+
this.state.gui_states.viewConfig.defaultPaintCursor;
|
|
442
|
+
this.state.protectedData.canvases.drawingCanvas.oncontextmenu = () => false;
|
|
709
443
|
/**
|
|
710
|
-
*
|
|
711
|
-
* it should be hide, so we don't need to add it to mainAreaContainer
|
|
444
|
+
* layer canvases
|
|
712
445
|
*/
|
|
713
|
-
this.protectedData.canvases.drawingCanvasLayerMaster.width =
|
|
714
|
-
this.nrrd_states.changedWidth;
|
|
715
|
-
this.protectedData.canvases.drawingCanvasLayerMaster.height =
|
|
716
|
-
this.nrrd_states.changedHeight;
|
|
717
|
-
for (const [, target] of this.protectedData.layerTargets) {
|
|
718
|
-
target.canvas.width = this.nrrd_states.changedWidth;
|
|
719
|
-
target.canvas.height = this.nrrd_states.changedHeight;
|
|
446
|
+
this.state.protectedData.canvases.drawingCanvasLayerMaster.width =
|
|
447
|
+
this.state.nrrd_states.view.changedWidth;
|
|
448
|
+
this.state.protectedData.canvases.drawingCanvasLayerMaster.height =
|
|
449
|
+
this.state.nrrd_states.view.changedHeight;
|
|
450
|
+
for (const [, target] of this.state.protectedData.layerTargets) {
|
|
451
|
+
target.canvas.width = this.state.nrrd_states.view.changedWidth;
|
|
452
|
+
target.canvas.height = this.state.nrrd_states.view.changedHeight;
|
|
720
453
|
}
|
|
721
454
|
/**
|
|
722
455
|
* display and drawing canvas container
|
|
723
456
|
*/
|
|
724
|
-
// this.mainAreaContainer.style.width = this.nrrd_states.changedWidth + "px";
|
|
725
|
-
// this.mainAreaContainer.style.height = this.nrrd_states.changedHeight + "px";
|
|
726
457
|
this.mainAreaContainer.style.width =
|
|
727
|
-
this.nrrd_states.originWidth * 8 + "px";
|
|
458
|
+
this.state.nrrd_states.image.originWidth * 8 + "px";
|
|
728
459
|
this.mainAreaContainer.style.height =
|
|
729
|
-
this.nrrd_states.originHeight * 8 + "px";
|
|
730
|
-
this.mainAreaContainer.appendChild(this.protectedData.canvases.displayCanvas);
|
|
731
|
-
this.mainAreaContainer.appendChild(this.protectedData.canvases.drawingCanvas);
|
|
460
|
+
this.state.nrrd_states.image.originHeight * 8 + "px";
|
|
461
|
+
this.mainAreaContainer.appendChild(this.state.protectedData.canvases.displayCanvas);
|
|
462
|
+
this.mainAreaContainer.appendChild(this.state.protectedData.canvases.drawingCanvas);
|
|
732
463
|
}
|
|
733
464
|
useEraser() {
|
|
734
465
|
return this.eraserTool.createClearArc();
|
|
@@ -748,13 +479,9 @@ export class DrawToolCore extends CommToolsData {
|
|
|
748
479
|
this.crosshairTool.enableCrosshair();
|
|
749
480
|
}
|
|
750
481
|
drawImageOnEmptyImage(canvas) {
|
|
751
|
-
const ctx = this.protectedData.ctxes.emptyCtx;
|
|
752
|
-
const w = this.protectedData.canvases.emptyCanvas.width;
|
|
753
|
-
const h = this.protectedData.canvases.emptyCanvas.height;
|
|
754
|
-
// No flip needed: the layer canvas screen coordinates already match the
|
|
755
|
-
// Three.js source coordinate system used by MaskVolume. Applying a flip
|
|
756
|
-
// here would invert cross-axis slice indices (e.g. coronal slice 220
|
|
757
|
-
// becoming 228 when total=448).
|
|
482
|
+
const ctx = this.state.protectedData.ctxes.emptyCtx;
|
|
483
|
+
const w = this.state.protectedData.canvases.emptyCanvas.width;
|
|
484
|
+
const h = this.state.protectedData.canvases.emptyCanvas.height;
|
|
758
485
|
ctx.imageSmoothingEnabled = false;
|
|
759
486
|
ctx.drawImage(canvas, 0, 0, w, h);
|
|
760
487
|
}
|
|
@@ -762,9 +489,6 @@ export class DrawToolCore extends CommToolsData {
|
|
|
762
489
|
drawCalculatorSphereOnEachViews(axis) {
|
|
763
490
|
this.sphereTool.drawCalculatorSphereOnEachViews(axis);
|
|
764
491
|
}
|
|
765
|
-
drawSphereOnEachViews(decay, axis) {
|
|
766
|
-
this.sphereTool.drawSphereOnEachViews(decay, axis);
|
|
767
|
-
}
|
|
768
492
|
configMouseSphereWheel() {
|
|
769
493
|
return this.sphereTool.configMouseSphereWheel();
|
|
770
494
|
}
|
|
@@ -774,6 +498,14 @@ export class DrawToolCore extends CommToolsData {
|
|
|
774
498
|
drawSphere(mouseX, mouseY, radius) {
|
|
775
499
|
this.sphereTool.drawSphere(mouseX, mouseY, radius);
|
|
776
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* Refresh sphere canvas from sphereMaskVolume for the current slice/axis.
|
|
503
|
+
*/
|
|
504
|
+
refreshSphereOverlay() {
|
|
505
|
+
if (this.state.gui_states.mode.sphere) {
|
|
506
|
+
this.sphereTool.refreshSphereCanvas();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
777
509
|
/**
|
|
778
510
|
* Convert cursor point between axis views.
|
|
779
511
|
* Delegated to CrosshairTool.
|
|
@@ -786,40 +518,30 @@ export class DrawToolCore extends CommToolsData {
|
|
|
786
518
|
}
|
|
787
519
|
/****************************layer div controls****************************************************/
|
|
788
520
|
getRestLayer() {
|
|
789
|
-
const layers = this.nrrd_states.layers;
|
|
521
|
+
const layers = this.state.nrrd_states.image.layers;
|
|
790
522
|
const restLayer = layers.filter((item) => {
|
|
791
|
-
return item !== this.gui_states.layer;
|
|
523
|
+
return item !== this.state.gui_states.layerChannel.layer;
|
|
792
524
|
});
|
|
793
525
|
return restLayer;
|
|
794
526
|
}
|
|
795
527
|
/**************************** Undo/Redo functions (Phase 6 — Delta-based) ****************************/
|
|
796
528
|
/**
|
|
797
|
-
* Clear mask on current slice canvas.
|
|
798
|
-
*
|
|
799
|
-
* Only clears the active layer's MaskVolume slice data.
|
|
800
|
-
* Other layers are left untouched. After clearing, all layer canvases
|
|
801
|
-
* are re-rendered from MaskVolume to keep visuals in sync.
|
|
529
|
+
* Clear the mask on the current slice canvas for the active layer ONLY.
|
|
802
530
|
*/
|
|
803
|
-
|
|
804
|
-
this.protectedData.
|
|
805
|
-
this.protectedData.canvases.originCanvas.width =
|
|
806
|
-
this.protectedData.canvases.originCanvas.width;
|
|
807
|
-
this.protectedData.mainPreSlices.repaint.call(this.protectedData.mainPreSlices);
|
|
808
|
-
this.protectedData.previousDrawingImage =
|
|
809
|
-
this.protectedData.ctxes.emptyCtx.createImageData(1, 1);
|
|
531
|
+
clearActiveSlice() {
|
|
532
|
+
this.state.protectedData.isDrawing = true;
|
|
533
|
+
this.state.protectedData.canvases.originCanvas.width =
|
|
534
|
+
this.state.protectedData.canvases.originCanvas.width;
|
|
535
|
+
this.state.protectedData.mainPreSlices.repaint.call(this.state.protectedData.mainPreSlices);
|
|
810
536
|
// Clear only the active layer's MaskVolume slice and record undo delta
|
|
811
537
|
try {
|
|
812
|
-
const axis = this.protectedData.axis;
|
|
813
|
-
const idx = this.nrrd_states.
|
|
814
|
-
const activeLayer = this.gui_states.layer;
|
|
815
|
-
const vol = this.getVolumeForLayer(activeLayer);
|
|
816
|
-
// Capture old slice for undo before clearing
|
|
538
|
+
const axis = this.state.protectedData.axis;
|
|
539
|
+
const idx = this.state.nrrd_states.view.currentSliceIndex;
|
|
540
|
+
const activeLayer = this.state.gui_states.layerChannel.layer;
|
|
541
|
+
const vol = this.renderer.getVolumeForLayer(activeLayer);
|
|
817
542
|
const oldSlice = vol.getSliceUint8(idx, axis).data.slice();
|
|
818
|
-
// Clear only the active layer's MaskVolume slice
|
|
819
543
|
vol.clearSlice(idx, axis);
|
|
820
|
-
// New (all-zero) slice for undo newSlice
|
|
821
544
|
const { data: newSlice, width, height } = vol.getSliceUint8(idx, axis);
|
|
822
|
-
// Push clearPaint delta to UndoManager (supports undo)
|
|
823
545
|
this.undoManager.push({
|
|
824
546
|
layerId: activeLayer,
|
|
825
547
|
axis,
|
|
@@ -827,123 +549,149 @@ export class DrawToolCore extends CommToolsData {
|
|
|
827
549
|
oldSlice,
|
|
828
550
|
newSlice: newSlice.slice(),
|
|
829
551
|
});
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
this.nrrd_states.getMask(newSlice, activeLayer, activeChannel, idx, axis, width, height, true // clearFlag = true
|
|
834
|
-
);
|
|
552
|
+
if (!this.state.nrrd_states.flags.loadingMaskData && !this.state.gui_states.mode.sphere) {
|
|
553
|
+
const activeChannel = this.state.gui_states.layerChannel.activeChannel || 1;
|
|
554
|
+
this.state.annotationCallbacks.onMaskChanged(newSlice, activeLayer, activeChannel, idx, axis, width, height, true);
|
|
835
555
|
}
|
|
836
556
|
}
|
|
837
557
|
catch (_a) {
|
|
838
558
|
// Volume not ready (1×1×1 placeholder)
|
|
839
559
|
}
|
|
840
|
-
// Re-render ALL layers from MaskVolume to canvas (rebuilds visuals from source of truth)
|
|
841
560
|
this.resetLayerCanvas();
|
|
842
|
-
const buffer = this.getOrCreateSliceBuffer(this.protectedData.axis);
|
|
561
|
+
const buffer = this.renderer.getOrCreateSliceBuffer(this.state.protectedData.axis);
|
|
843
562
|
if (buffer) {
|
|
844
|
-
const w = this.nrrd_states.changedWidth;
|
|
845
|
-
const h = this.nrrd_states.changedHeight;
|
|
846
|
-
for (const layerId of this.nrrd_states.layers) {
|
|
847
|
-
const target = this.protectedData.layerTargets.get(layerId);
|
|
563
|
+
const w = this.state.nrrd_states.view.changedWidth;
|
|
564
|
+
const h = this.state.nrrd_states.view.changedHeight;
|
|
565
|
+
for (const layerId of this.state.nrrd_states.image.layers) {
|
|
566
|
+
const target = this.state.protectedData.layerTargets.get(layerId);
|
|
848
567
|
if (!target)
|
|
849
568
|
continue;
|
|
850
569
|
target.ctx.clearRect(0, 0, w, h);
|
|
851
|
-
this.renderSliceToCanvas(layerId, this.protectedData.axis, this.nrrd_states.
|
|
570
|
+
this.renderer.renderSliceToCanvas(layerId, this.state.protectedData.axis, this.state.nrrd_states.view.currentSliceIndex, buffer, target.ctx, w, h);
|
|
852
571
|
}
|
|
853
572
|
}
|
|
854
|
-
this.compositeAllLayers();
|
|
573
|
+
this.renderer.compositeAllLayers();
|
|
855
574
|
this.setIsDrawFalse(1000);
|
|
856
575
|
}
|
|
857
576
|
/**
|
|
858
577
|
* Undo the last drawing operation on the active layer.
|
|
859
|
-
* Restores the MaskVolume slice to its pre-draw state, re-renders
|
|
860
|
-
* the canvas, and notifies the backend.
|
|
861
578
|
*/
|
|
862
579
|
undoLastPainting() {
|
|
863
580
|
const delta = this.undoManager.undo();
|
|
864
581
|
if (!delta)
|
|
865
582
|
return;
|
|
866
583
|
try {
|
|
867
|
-
const vol = this.getVolumeForLayer(delta.layerId);
|
|
584
|
+
const vol = this.renderer.getVolumeForLayer(delta.layerId);
|
|
868
585
|
vol.setSliceUint8(delta.sliceIndex, delta.oldSlice, delta.axis);
|
|
869
586
|
}
|
|
870
587
|
catch (_a) {
|
|
871
|
-
return;
|
|
588
|
+
return;
|
|
872
589
|
}
|
|
873
|
-
this.protectedData.
|
|
874
|
-
if (delta.axis === this.protectedData.axis && delta.sliceIndex === this.nrrd_states.
|
|
590
|
+
this.state.protectedData.isDrawing = true;
|
|
591
|
+
if (delta.axis === this.state.protectedData.axis && delta.sliceIndex === this.state.nrrd_states.view.currentSliceIndex) {
|
|
875
592
|
this.applyUndoRedoToCanvas(delta.layerId);
|
|
876
593
|
}
|
|
877
|
-
if (!this.nrrd_states.
|
|
878
|
-
const { data: sliceData, width, height } = this.getVolumeForLayer(delta.layerId)
|
|
594
|
+
if (!this.state.nrrd_states.flags.loadingMaskData) {
|
|
595
|
+
const { data: sliceData, width, height } = this.renderer.getVolumeForLayer(delta.layerId)
|
|
879
596
|
.getSliceUint8(delta.sliceIndex, delta.axis);
|
|
880
|
-
this.
|
|
597
|
+
this.state.annotationCallbacks.onMaskChanged(sliceData, delta.layerId, this.state.gui_states.layerChannel.activeChannel || 1, delta.sliceIndex, delta.axis, width, height, false);
|
|
881
598
|
}
|
|
882
599
|
this.setIsDrawFalse(1000);
|
|
883
600
|
}
|
|
884
601
|
/**
|
|
885
602
|
* Redo the last undone operation on the active layer.
|
|
886
|
-
* Reapplies the MaskVolume slice to its post-draw state, re-renders
|
|
887
|
-
* the canvas, and notifies the backend.
|
|
888
603
|
*/
|
|
889
604
|
redoLastPainting() {
|
|
890
605
|
const delta = this.undoManager.redo();
|
|
891
606
|
if (!delta)
|
|
892
607
|
return;
|
|
893
608
|
try {
|
|
894
|
-
const vol = this.getVolumeForLayer(delta.layerId);
|
|
609
|
+
const vol = this.renderer.getVolumeForLayer(delta.layerId);
|
|
895
610
|
vol.setSliceUint8(delta.sliceIndex, delta.newSlice, delta.axis);
|
|
896
611
|
}
|
|
897
612
|
catch (_a) {
|
|
898
|
-
return;
|
|
613
|
+
return;
|
|
899
614
|
}
|
|
900
|
-
this.protectedData.
|
|
901
|
-
if (delta.axis === this.protectedData.axis && delta.sliceIndex === this.nrrd_states.
|
|
615
|
+
this.state.protectedData.isDrawing = true;
|
|
616
|
+
if (delta.axis === this.state.protectedData.axis && delta.sliceIndex === this.state.nrrd_states.view.currentSliceIndex) {
|
|
902
617
|
this.applyUndoRedoToCanvas(delta.layerId);
|
|
903
618
|
}
|
|
904
|
-
if (!this.nrrd_states.
|
|
905
|
-
const { data: sliceData, width, height } = this.getVolumeForLayer(delta.layerId)
|
|
619
|
+
if (!this.state.nrrd_states.flags.loadingMaskData) {
|
|
620
|
+
const { data: sliceData, width, height } = this.renderer.getVolumeForLayer(delta.layerId)
|
|
906
621
|
.getSliceUint8(delta.sliceIndex, delta.axis);
|
|
907
|
-
this.
|
|
622
|
+
this.state.annotationCallbacks.onMaskChanged(sliceData, delta.layerId, this.state.gui_states.layerChannel.activeChannel || 1, delta.sliceIndex, delta.axis, width, height, false);
|
|
908
623
|
}
|
|
909
624
|
this.setIsDrawFalse(1000);
|
|
910
625
|
}
|
|
911
626
|
/**
|
|
912
627
|
* Re-render a layer canvas from MaskVolume and composite to master.
|
|
913
|
-
* Called after writing oldSlice/newSlice back to the volume during undo/redo.
|
|
914
628
|
*/
|
|
915
629
|
applyUndoRedoToCanvas(layerId) {
|
|
916
|
-
let target = this.protectedData.layerTargets.get(layerId);
|
|
630
|
+
let target = this.state.protectedData.layerTargets.get(layerId);
|
|
917
631
|
if (!target) {
|
|
918
|
-
const firstId = this.nrrd_states.layers[0];
|
|
919
|
-
target = this.protectedData.layerTargets.get(firstId);
|
|
632
|
+
const firstId = this.state.nrrd_states.image.layers[0];
|
|
633
|
+
target = this.state.protectedData.layerTargets.get(firstId);
|
|
920
634
|
}
|
|
921
635
|
const { ctx, canvas } = target;
|
|
922
|
-
// Clear and re-render the affected layer canvas from MaskVolume
|
|
923
636
|
canvas.width = canvas.width;
|
|
924
|
-
const buffer = this.getOrCreateSliceBuffer(this.protectedData.axis);
|
|
637
|
+
const buffer = this.renderer.getOrCreateSliceBuffer(this.state.protectedData.axis);
|
|
925
638
|
if (buffer) {
|
|
926
|
-
this.renderSliceToCanvas(layerId, this.protectedData.axis, this.nrrd_states.
|
|
639
|
+
this.renderer.renderSliceToCanvas(layerId, this.state.protectedData.axis, this.state.nrrd_states.view.currentSliceIndex, buffer, ctx, this.state.nrrd_states.view.changedWidth, this.state.nrrd_states.view.changedHeight);
|
|
927
640
|
}
|
|
928
|
-
|
|
929
|
-
this.compositeAllLayers();
|
|
930
|
-
// Update previousDrawingImage from master
|
|
931
|
-
this.protectedData.previousDrawingImage =
|
|
932
|
-
this.protectedData.ctxes.drawingLayerMasterCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvasLayerMaster.width, this.protectedData.canvases.drawingCanvasLayerMaster.height);
|
|
641
|
+
this.renderer.compositeAllLayers();
|
|
933
642
|
}
|
|
934
643
|
/****************************Store images (delegated to ImageStoreHelper)****************************************************/
|
|
935
|
-
|
|
936
|
-
this.imageStoreHelper.
|
|
937
|
-
}
|
|
938
|
-
storeImageToLayer(index, canvas) {
|
|
939
|
-
return this.imageStoreHelper.storeImageToLayer(index, canvas);
|
|
940
|
-
}
|
|
941
|
-
storeEachLayerImage(index, layer) {
|
|
942
|
-
this.imageStoreHelper.storeEachLayerImage(index, layer);
|
|
644
|
+
syncLayerSliceData(index, layer) {
|
|
645
|
+
this.imageStoreHelper.syncLayerSliceData(index, layer);
|
|
943
646
|
}
|
|
944
647
|
/******************************** Utils gui related functions (delegated to ContrastTool) ***************************************/
|
|
945
648
|
setupConrastEvents(callback) {
|
|
946
649
|
this.contrastTool.setupConrastEvents(callback);
|
|
947
650
|
}
|
|
651
|
+
// ── Pseudo-abstract methods (overridden by NrrdTools via callback injection) ──
|
|
652
|
+
/** Override in NrrdTools */
|
|
653
|
+
setIsDrawFalse(target) {
|
|
654
|
+
throw new Error("setIsDrawFalse must be provided by NrrdTools");
|
|
655
|
+
}
|
|
656
|
+
/** Override in NrrdTools */
|
|
657
|
+
setSyncsliceNum() {
|
|
658
|
+
throw new Error("setSyncsliceNum must be provided by NrrdTools");
|
|
659
|
+
}
|
|
660
|
+
/** Override in NrrdTools */
|
|
661
|
+
resetPaintAreaUIPosition(l, t) {
|
|
662
|
+
throw new Error("resetPaintAreaUIPosition must be provided by NrrdTools");
|
|
663
|
+
}
|
|
664
|
+
/** Override in NrrdTools */
|
|
665
|
+
resizePaintArea(factor) {
|
|
666
|
+
throw new Error("resizePaintArea must be provided by NrrdTools");
|
|
667
|
+
}
|
|
668
|
+
/** Override in NrrdTools */
|
|
669
|
+
setEmptyCanvasSize(axis) {
|
|
670
|
+
throw new Error("setEmptyCanvasSize must be provided by NrrdTools");
|
|
671
|
+
}
|
|
672
|
+
/** Override in NrrdTools */
|
|
673
|
+
flipDisplayImageByAxis() {
|
|
674
|
+
throw new Error("flipDisplayImageByAxis must be provided by NrrdTools");
|
|
675
|
+
}
|
|
676
|
+
/** Override in NrrdTools */
|
|
677
|
+
updateOriginAndChangedWH() {
|
|
678
|
+
throw new Error("updateOriginAndChangedWH must be provided by NrrdTools");
|
|
679
|
+
}
|
|
680
|
+
/** Override in NrrdTools */
|
|
681
|
+
resetLayerCanvas() {
|
|
682
|
+
throw new Error("resetLayerCanvas must be provided by NrrdTools");
|
|
683
|
+
}
|
|
684
|
+
/** Override in NrrdTools */
|
|
685
|
+
redrawDisplayCanvas() {
|
|
686
|
+
throw new Error("redrawDisplayCanvas must be provided by NrrdTools");
|
|
687
|
+
}
|
|
688
|
+
/** Override in NrrdTools */
|
|
689
|
+
enterSphereMode() {
|
|
690
|
+
throw new Error("enterSphereMode must be provided by NrrdTools");
|
|
691
|
+
}
|
|
692
|
+
/** Override in NrrdTools */
|
|
693
|
+
exitSphereMode() {
|
|
694
|
+
throw new Error("exitSphereMode must be provided by NrrdTools");
|
|
695
|
+
}
|
|
948
696
|
}
|
|
949
697
|
//# sourceMappingURL=DrawToolCore.js.map
|