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.
Files changed (134) hide show
  1. package/README.md +197 -875
  2. package/dist/Utils/segmentation/CanvasState.d.ts +31 -0
  3. package/dist/Utils/segmentation/CanvasState.js +167 -0
  4. package/dist/Utils/segmentation/CanvasState.js.map +1 -0
  5. package/dist/Utils/segmentation/DragOperator.d.ts +10 -2
  6. package/dist/Utils/segmentation/DragOperator.js +16 -8
  7. package/dist/Utils/segmentation/DragOperator.js.map +1 -1
  8. package/dist/Utils/segmentation/DrawToolCore.d.ts +64 -35
  9. package/dist/Utils/segmentation/DrawToolCore.js +332 -584
  10. package/dist/Utils/segmentation/DrawToolCore.js.map +1 -1
  11. package/dist/Utils/segmentation/NrrdTools.d.ts +117 -353
  12. package/dist/Utils/segmentation/NrrdTools.js +622 -1081
  13. package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
  14. package/dist/Utils/segmentation/RenderingUtils.d.ts +73 -0
  15. package/dist/Utils/segmentation/RenderingUtils.js +180 -0
  16. package/dist/Utils/segmentation/RenderingUtils.js.map +1 -0
  17. package/dist/Utils/segmentation/core/MaskVolume.js +23 -2
  18. package/dist/Utils/segmentation/core/MaskVolume.js.map +1 -1
  19. package/dist/Utils/segmentation/core/UndoManager.d.ts +1 -1
  20. package/dist/Utils/segmentation/core/UndoManager.js +1 -1
  21. package/dist/Utils/segmentation/core/index.d.ts +1 -3
  22. package/dist/Utils/segmentation/core/index.js +0 -1
  23. package/dist/Utils/segmentation/core/index.js.map +1 -1
  24. package/dist/Utils/segmentation/core/types.d.ts +384 -0
  25. package/dist/Utils/segmentation/core/types.js +25 -24
  26. package/dist/Utils/segmentation/core/types.js.map +1 -1
  27. package/dist/Utils/segmentation/coreTools/GuiState.d.ts +22 -0
  28. package/dist/Utils/segmentation/coreTools/GuiState.js +48 -0
  29. package/dist/Utils/segmentation/coreTools/GuiState.js.map +1 -0
  30. package/dist/Utils/segmentation/coreTools/NrrdState.d.ts +25 -0
  31. package/dist/Utils/segmentation/coreTools/NrrdState.js +85 -0
  32. package/dist/Utils/segmentation/coreTools/NrrdState.js.map +1 -0
  33. package/dist/Utils/segmentation/coreTools/divControlTools.d.ts +1 -1
  34. package/dist/Utils/segmentation/coreTools/divControlTools.js +1 -1
  35. package/dist/Utils/segmentation/coreTools/divControlTools.js.map +1 -1
  36. package/dist/Utils/segmentation/coreTools/gui.d.ts +12 -12
  37. package/dist/Utils/segmentation/coreTools/gui.js +81 -107
  38. package/dist/Utils/segmentation/coreTools/gui.js.map +1 -1
  39. package/dist/Utils/segmentation/eventRouter/EventRouter.d.ts +2 -1
  40. package/dist/Utils/segmentation/eventRouter/EventRouter.js +15 -10
  41. package/dist/Utils/segmentation/eventRouter/EventRouter.js.map +1 -1
  42. package/dist/Utils/segmentation/eventRouter/types.d.ts +3 -3
  43. package/dist/Utils/segmentation/tools/BaseTool.d.ts +8 -5
  44. package/dist/Utils/segmentation/tools/BaseTool.js +1 -2
  45. package/dist/Utils/segmentation/tools/BaseTool.js.map +1 -1
  46. package/dist/Utils/segmentation/tools/ContrastTool.d.ts +3 -6
  47. package/dist/Utils/segmentation/tools/ContrastTool.js.map +1 -1
  48. package/dist/Utils/segmentation/tools/CrosshairTool.d.ts +11 -1
  49. package/dist/Utils/segmentation/tools/CrosshairTool.js +50 -25
  50. package/dist/Utils/segmentation/tools/CrosshairTool.js.map +1 -1
  51. package/dist/Utils/segmentation/tools/DataLoader.d.ts +33 -0
  52. package/dist/Utils/segmentation/tools/DataLoader.js +159 -0
  53. package/dist/Utils/segmentation/tools/DataLoader.js.map +1 -0
  54. package/dist/Utils/segmentation/tools/DragSliceTool.d.ts +3 -10
  55. package/dist/Utils/segmentation/tools/DragSliceTool.js +63 -53
  56. package/dist/Utils/segmentation/tools/DragSliceTool.js.map +1 -1
  57. package/dist/Utils/segmentation/tools/DrawingTool.d.ts +85 -0
  58. package/dist/Utils/segmentation/tools/DrawingTool.js +270 -0
  59. package/dist/Utils/segmentation/tools/DrawingTool.js.map +1 -0
  60. package/dist/Utils/segmentation/tools/EraserTool.js +8 -8
  61. package/dist/Utils/segmentation/tools/EraserTool.js.map +1 -1
  62. package/dist/Utils/segmentation/tools/ImageStoreHelper.d.ts +13 -33
  63. package/dist/Utils/segmentation/tools/ImageStoreHelper.js +39 -82
  64. package/dist/Utils/segmentation/tools/ImageStoreHelper.js.map +1 -1
  65. package/dist/Utils/segmentation/tools/LayerChannelManager.d.ts +52 -0
  66. package/dist/Utils/segmentation/tools/LayerChannelManager.js +175 -0
  67. package/dist/Utils/segmentation/tools/LayerChannelManager.js.map +1 -0
  68. package/dist/Utils/segmentation/tools/PanTool.d.ts +47 -0
  69. package/dist/Utils/segmentation/tools/PanTool.js +90 -0
  70. package/dist/Utils/segmentation/tools/PanTool.js.map +1 -0
  71. package/dist/Utils/segmentation/tools/SliceRenderPipeline.d.ts +80 -0
  72. package/dist/Utils/segmentation/tools/SliceRenderPipeline.js +355 -0
  73. package/dist/Utils/segmentation/tools/SliceRenderPipeline.js.map +1 -0
  74. package/dist/Utils/segmentation/tools/SphereTool.d.ts +223 -10
  75. package/dist/Utils/segmentation/tools/SphereTool.js +512 -47
  76. package/dist/Utils/segmentation/tools/SphereTool.js.map +1 -1
  77. package/dist/Utils/segmentation/tools/ToolHost.d.ts +74 -0
  78. package/dist/Utils/segmentation/tools/ToolHost.js +10 -0
  79. package/dist/Utils/segmentation/tools/ToolHost.js.map +1 -0
  80. package/dist/Utils/segmentation/tools/ZoomTool.d.ts +2 -6
  81. package/dist/Utils/segmentation/tools/ZoomTool.js +3 -3
  82. package/dist/Utils/segmentation/tools/ZoomTool.js.map +1 -1
  83. package/dist/Utils/segmentation/tools/index.d.ts +8 -6
  84. package/dist/Utils/segmentation/tools/index.js +10 -1
  85. package/dist/Utils/segmentation/tools/index.js.map +1 -1
  86. package/dist/Utils/workers/reformatSaveDataWorker.d.ts +1 -1
  87. package/dist/bundle.esm.js +3729 -3017
  88. package/dist/bundle.umd.js +3729 -3017
  89. package/dist/index.d.ts +6 -3
  90. package/dist/index.js +1 -1
  91. package/dist/index.js.map +1 -1
  92. package/dist/types/Utils/segmentation/CanvasState.d.ts +31 -0
  93. package/dist/types/Utils/segmentation/DragOperator.d.ts +10 -2
  94. package/dist/types/Utils/segmentation/DrawToolCore.d.ts +64 -35
  95. package/dist/types/Utils/segmentation/NrrdTools.d.ts +117 -353
  96. package/dist/types/Utils/segmentation/RenderingUtils.d.ts +73 -0
  97. package/dist/types/Utils/segmentation/core/UndoManager.d.ts +1 -1
  98. package/dist/types/Utils/segmentation/core/index.d.ts +1 -3
  99. package/dist/types/Utils/segmentation/core/types.d.ts +384 -0
  100. package/dist/types/Utils/segmentation/coreTools/GuiState.d.ts +22 -0
  101. package/dist/types/Utils/segmentation/coreTools/NrrdState.d.ts +25 -0
  102. package/dist/types/Utils/segmentation/coreTools/divControlTools.d.ts +1 -1
  103. package/dist/types/Utils/segmentation/coreTools/gui.d.ts +12 -12
  104. package/dist/types/Utils/segmentation/eventRouter/EventRouter.d.ts +2 -1
  105. package/dist/types/Utils/segmentation/eventRouter/types.d.ts +3 -3
  106. package/dist/types/Utils/segmentation/tools/BaseTool.d.ts +8 -5
  107. package/dist/types/Utils/segmentation/tools/ContrastTool.d.ts +3 -6
  108. package/dist/types/Utils/segmentation/tools/CrosshairTool.d.ts +11 -1
  109. package/dist/types/Utils/segmentation/tools/DataLoader.d.ts +33 -0
  110. package/dist/types/Utils/segmentation/tools/DragSliceTool.d.ts +3 -10
  111. package/dist/types/Utils/segmentation/tools/DrawingTool.d.ts +85 -0
  112. package/dist/types/Utils/segmentation/tools/ImageStoreHelper.d.ts +13 -33
  113. package/dist/types/Utils/segmentation/tools/LayerChannelManager.d.ts +52 -0
  114. package/dist/types/Utils/segmentation/tools/PanTool.d.ts +47 -0
  115. package/dist/types/Utils/segmentation/tools/SliceRenderPipeline.d.ts +80 -0
  116. package/dist/types/Utils/segmentation/tools/SphereTool.d.ts +223 -10
  117. package/dist/types/Utils/segmentation/tools/ToolHost.d.ts +74 -0
  118. package/dist/types/Utils/segmentation/tools/ZoomTool.d.ts +2 -6
  119. package/dist/types/Utils/segmentation/tools/index.d.ts +8 -6
  120. package/dist/types/Utils/workers/reformatSaveDataWorker.d.ts +1 -1
  121. package/dist/types/index.d.ts +6 -3
  122. package/package.json +1 -1
  123. package/dist/Utils/segmentation/CommToolsData.d.ts +0 -177
  124. package/dist/Utils/segmentation/CommToolsData.js +0 -548
  125. package/dist/Utils/segmentation/CommToolsData.js.map +0 -1
  126. package/dist/Utils/segmentation/core/MigrationUtils.d.ts +0 -97
  127. package/dist/Utils/segmentation/core/MigrationUtils.js +0 -163
  128. package/dist/Utils/segmentation/core/MigrationUtils.js.map +0 -1
  129. package/dist/Utils/segmentation/coreTools/coreType.d.ts +0 -363
  130. package/dist/Utils/segmentation/coreTools/coreType.js +0 -3
  131. package/dist/Utils/segmentation/coreTools/coreType.js.map +0 -1
  132. package/dist/types/Utils/segmentation/CommToolsData.d.ts +0 -177
  133. package/dist/types/Utils/segmentation/core/MigrationUtils.d.ts +0 -97
  134. package/dist/types/Utils/segmentation/coreTools/coreType.d.ts +0 -363
@@ -1,17 +1,26 @@
1
- import { CommToolsData } from "./CommToolsData";
2
- import { switchEraserSize, switchPencilIcon } from "../utils";
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
- export class DrawToolCore extends CommToolsData {
12
- constructor(container, options) {
13
- const mainAreaContainer = document.createElement("div");
14
- super(container, mainAreaContainer, options);
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
- /** Snapshot of the active layer's slice captured on mouse-down (before drawing). */
41
- this.preDrawSlice = null;
42
- this.preDrawAxis = "z";
43
- this.preDrawSliceIndex = 0;
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
- storeImageToAxis: (index, imageData, axis) => this.imageStoreHelper.storeImageToAxis(index, imageData, axis),
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.Is_Draw = false;
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 class field
126
- this.eventRouter.setKeyboardSettings(this._keyboardSettings);
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
- var _a, _b;
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._keyboardSettings.undo) {
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._keyboardSettings.redo;
141
- const undoKeyUpper = this._keyboardSettings.undo.toUpperCase();
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._keyboardSettings.crosshair) {
147
- if (!this.gui_states.sphere && !this.gui_states.calculator) {
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
- // EventRouter's handleKeyDown will enforce mutual exclusion
153
- if (ev.key === this._keyboardSettings.draw && !this.gui_states.sphere && !this.gui_states.calculator) {
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
- // EventRouter will set mode to 'draw' via internal handler
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
- var _a, _b, _c, _d, _e, _f;
162
- if (this._configKeyBoard)
185
+ if (this.state.configKeyBoard)
163
186
  return;
164
187
  // Handle Ctrl key release (contrast mode toggle)
165
- if (this._keyboardSettings.contrast.includes(ev.key)) {
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 (!((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.isContrastEnabled()))
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
- // Block contrast toggle during crosshair or draw (mutual exclusion)
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 (((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.getMode()) !== 'contrast') {
179
- (_e = this.eventRouter) === null || _e === void 0 ? void 0 : _e.setMode('contrast');
203
+ if (this.eventRouter.getMode() !== 'contrast') {
204
+ this.eventRouter.setMode('contrast');
180
205
  }
181
206
  else {
182
- (_f = this.eventRouter) === null || _f === void 0 ? void 0 : _f.setMode('idle');
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
- // Fallback to first layer
203
- const firstId = this.nrrd_states.layers[0];
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
- this.nrrd_states.getMask = opts === null || opts === void 0 ? void 0 : opts.getMaskData;
211
- if (opts === null || opts === void 0 ? void 0 : opts.onClearLayerVolume) {
212
- this.nrrd_states.onClearLayerVolume = opts.onClearLayerVolume;
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.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
268
- }
269
- clearSpherePrintStoreImages() {
270
- this.sphereTool.clearSpherePrintStoreImages();
275
+ this.activeWheelMode = 'zoom';
271
276
  }
272
277
  paintOnCanvas() {
273
278
  var _a, _b, _c;
274
- /**
275
- * drag paint panel
276
- */
277
- let leftclicked = false;
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
- this.protectedData.previousDrawingImage =
295
- this.protectedData.ctxes.drawingCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvas.width, this.protectedData.canvases.drawingCanvas.height);
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
- // pan move
313
- this.drawingPrameters.handleOnPanMouseMove = (e) => {
314
- this.protectedData.canvases.drawingCanvas.style.cursor = "grabbing";
315
- this.nrrd_states.previousPanelL = e.clientX - panelMoveInnerX;
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.protectedData.Is_Draw = true;
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
- var _a, _b, _c, _d;
359
- if (leftclicked || rightclicked) {
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
- // when switch slice, clear previousDrawingImage
365
- // todo
366
- if (currentSliceIndex !== this.protectedData.mainPreSlices.index) {
367
- this.protectedData.previousDrawingImage =
368
- this.protectedData.ctxes.emptyCtx.createImageData(1, 1);
369
- currentSliceIndex = this.protectedData.mainPreSlices.index;
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 (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode()) === 'draw') {
375
- leftclicked = true;
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 ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled()) {
408
- this.nrrd_states.cursorPageX =
409
- e.offsetX / this.nrrd_states.sizeFoctor;
410
- this.nrrd_states.cursorPageY =
411
- e.offsetY / this.nrrd_states.sizeFoctor;
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 && !((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.isCrosshairEnabled())) {
416
- sphere(e);
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
- rightclicked = true;
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 for centralized event management
437
- // The handler is still the existing logic, just registered through EventRouter
438
- if (this.eventRouter) {
439
- this.eventRouter.setPointerDownHandler((e) => {
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 (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode()) === 'draw' || Is_Painting) {
486
- leftclicked = false;
487
- let { ctx, canvas } = this.setCurrentLayer();
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 ((this.gui_states.sphere || this.gui_states.calculator) &&
564
- ((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.isCrosshairEnabled())) {
565
- this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
566
- this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
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.calculator &&
569
- !((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.isCrosshairEnabled())) {
570
- // When mouse up
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
- rightclicked = false;
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
- var _a, _b;
612
- if (this.gui_states.readyToUpdate) {
613
- this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
614
- this.protectedData.ctxes.drawingCtx.globalAlpha =
615
- this.gui_states.globalAlpha;
616
- if (this.protectedData.Is_Draw) {
617
- this.protectedData.ctxes.drawingLayerMasterCtx.lineCap = "round";
618
- this.protectedData.ctxes.drawingLayerMasterCtx.globalAlpha = 1;
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
- // Use EventRouter mode for mutually exclusive crosshair vs draw rendering
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
- // Draw mode: show brush circle preview
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' || ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled())) {
643
- // Crosshair mode: show red cross lines (mutually exclusive with draw)
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
- // globalAlpha was set to gui_states.globalAlpha at the top of start().
652
- // Master stores full-alpha pixels; transparency applied here only.
653
- this.protectedData.ctxes.drawingCtx.drawImage(this.protectedData.canvases.drawingCanvasLayerMaster, 0, 0);
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
- drawLinesOnLayer(ctx, x, y) {
661
- ctx.beginPath();
662
- ctx.moveTo(this.nrrd_states.drawStartPos.x, this.nrrd_states.drawStartPos.y);
663
- if (this.gui_states.pencil) {
664
- ctx.strokeStyle = this.gui_states.color;
665
- ctx.lineWidth = this.gui_states.lineWidth;
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
- else {
668
- ctx.strokeStyle = this.gui_states.brushColor;
669
- ctx.lineWidth = this.gui_states.brushAndEraserSize;
410
+ this.panTool.onPointerLeave();
411
+ this.setIsDrawFalse(100);
412
+ if (this.state.gui_states.mode.pencil) {
413
+ this.setIsDrawFalse(1000);
670
414
  }
671
- ctx.lineTo(x, y);
672
- ctx.stroke();
673
- ctx.closePath();
674
- }
675
- paintOnCanvasLayer(x, y) {
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
- * layer1
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
- clearPaint() {
804
- this.protectedData.Is_Draw = true;
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.currentIndex;
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
- // Notify external that slice was cleared
831
- if (!this.nrrd_states.loadMaskJson && !this.gui_states.sphere && !this.gui_states.calculator) {
832
- const activeChannel = this.gui_states.activeChannel || 1;
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.currentIndex, buffer, target.ctx, w, h);
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; // Volume not ready
588
+ return;
872
589
  }
873
- this.protectedData.Is_Draw = true;
874
- if (delta.axis === this.protectedData.axis && delta.sliceIndex === this.nrrd_states.currentIndex) {
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.loadMaskJson) {
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.nrrd_states.getMask(sliceData, delta.layerId, this.gui_states.activeChannel || 1, delta.sliceIndex, delta.axis, width, height, false);
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; // Volume not ready
613
+ return;
899
614
  }
900
- this.protectedData.Is_Draw = true;
901
- if (delta.axis === this.protectedData.axis && delta.sliceIndex === this.nrrd_states.currentIndex) {
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.loadMaskJson) {
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.nrrd_states.getMask(sliceData, delta.layerId, this.gui_states.activeChannel || 1, delta.sliceIndex, delta.axis, width, height, false);
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.currentIndex, buffer, ctx, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
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
- // Re-composite all layers to master
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
- storeAllImages(index, layer) {
936
- this.imageStoreHelper.storeAllImages(index, layer);
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