copper3d 2.2.3 → 3.0.1

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 (111) hide show
  1. package/README.md +1056 -5
  2. package/dist/Scene/copperScene.d.ts +3 -0
  3. package/dist/Scene/copperScene.js +13 -0
  4. package/dist/Scene/copperScene.js.map +1 -1
  5. package/dist/Utils/segmentation/CommToolsData.d.ts +113 -11
  6. package/dist/Utils/segmentation/CommToolsData.js +314 -59
  7. package/dist/Utils/segmentation/CommToolsData.js.map +1 -1
  8. package/dist/Utils/segmentation/DragOperator.d.ts +13 -7
  9. package/dist/Utils/segmentation/DragOperator.js +51 -165
  10. package/dist/Utils/segmentation/DragOperator.js.map +1 -1
  11. package/dist/Utils/segmentation/DrawToolCore.d.ts +56 -76
  12. package/dist/Utils/segmentation/DrawToolCore.js +383 -891
  13. package/dist/Utils/segmentation/DrawToolCore.js.map +1 -1
  14. package/dist/Utils/segmentation/NrrdTools.d.ts +277 -15
  15. package/dist/Utils/segmentation/NrrdTools.js +600 -234
  16. package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
  17. package/dist/Utils/segmentation/core/MaskVolume.d.ts +444 -0
  18. package/dist/Utils/segmentation/core/MaskVolume.js +1117 -0
  19. package/dist/Utils/segmentation/core/MaskVolume.js.map +1 -0
  20. package/dist/Utils/segmentation/core/MigrationUtils.d.ts +97 -0
  21. package/dist/Utils/segmentation/core/MigrationUtils.js +163 -0
  22. package/dist/Utils/segmentation/core/MigrationUtils.js.map +1 -0
  23. package/dist/Utils/segmentation/core/UndoManager.d.ts +47 -0
  24. package/dist/Utils/segmentation/core/UndoManager.js +86 -0
  25. package/dist/Utils/segmentation/core/UndoManager.js.map +1 -0
  26. package/dist/Utils/segmentation/core/index.d.ts +76 -0
  27. package/dist/Utils/segmentation/core/index.js +75 -0
  28. package/dist/Utils/segmentation/core/index.js.map +1 -0
  29. package/dist/Utils/segmentation/core/types.d.ts +106 -0
  30. package/dist/Utils/segmentation/core/types.js +98 -0
  31. package/dist/Utils/segmentation/core/types.js.map +1 -0
  32. package/dist/Utils/segmentation/coreTools/coreType.d.ts +55 -39
  33. package/dist/Utils/segmentation/coreTools/coreType.js.map +1 -1
  34. package/dist/Utils/segmentation/coreTools/gui.d.ts +10 -17
  35. package/dist/Utils/segmentation/coreTools/gui.js +23 -27
  36. package/dist/Utils/segmentation/coreTools/gui.js.map +1 -1
  37. package/dist/Utils/segmentation/eventRouter/EventRouter.d.ts +145 -0
  38. package/dist/Utils/segmentation/eventRouter/EventRouter.js +375 -0
  39. package/dist/Utils/segmentation/eventRouter/EventRouter.js.map +1 -0
  40. package/dist/Utils/segmentation/eventRouter/index.d.ts +7 -0
  41. package/dist/Utils/segmentation/eventRouter/index.js +7 -0
  42. package/dist/Utils/segmentation/eventRouter/index.js.map +1 -0
  43. package/dist/Utils/segmentation/eventRouter/types.d.ts +70 -0
  44. package/dist/Utils/segmentation/eventRouter/types.js +7 -0
  45. package/dist/Utils/segmentation/eventRouter/types.js.map +1 -0
  46. package/dist/Utils/segmentation/tools/BaseTool.d.ts +28 -0
  47. package/dist/Utils/segmentation/tools/BaseTool.js +18 -0
  48. package/dist/Utils/segmentation/tools/BaseTool.js.map +1 -0
  49. package/dist/Utils/segmentation/tools/ContrastTool.d.ts +26 -0
  50. package/dist/Utils/segmentation/tools/ContrastTool.js +118 -0
  51. package/dist/Utils/segmentation/tools/ContrastTool.js.map +1 -0
  52. package/dist/Utils/segmentation/tools/CrosshairTool.d.ts +30 -0
  53. package/dist/Utils/segmentation/tools/CrosshairTool.js +157 -0
  54. package/dist/Utils/segmentation/tools/CrosshairTool.js.map +1 -0
  55. package/dist/Utils/segmentation/tools/DragSliceTool.d.ts +44 -0
  56. package/dist/Utils/segmentation/tools/DragSliceTool.js +170 -0
  57. package/dist/Utils/segmentation/tools/DragSliceTool.js.map +1 -0
  58. package/dist/Utils/segmentation/tools/EraserTool.d.ts +16 -0
  59. package/dist/Utils/segmentation/tools/EraserTool.js +92 -0
  60. package/dist/Utils/segmentation/tools/EraserTool.js.map +1 -0
  61. package/dist/Utils/segmentation/tools/ImageStoreHelper.d.ts +62 -0
  62. package/dist/Utils/segmentation/tools/ImageStoreHelper.js +160 -0
  63. package/dist/Utils/segmentation/tools/ImageStoreHelper.js.map +1 -0
  64. package/dist/Utils/segmentation/tools/SphereTool.d.ts +36 -0
  65. package/dist/Utils/segmentation/tools/SphereTool.js +193 -0
  66. package/dist/Utils/segmentation/tools/SphereTool.js.map +1 -0
  67. package/dist/Utils/segmentation/tools/ZoomTool.d.ts +20 -0
  68. package/dist/Utils/segmentation/tools/ZoomTool.js +60 -0
  69. package/dist/Utils/segmentation/tools/ZoomTool.js.map +1 -0
  70. package/dist/Utils/segmentation/tools/index.d.ts +22 -0
  71. package/dist/Utils/segmentation/tools/index.js +23 -0
  72. package/dist/Utils/segmentation/tools/index.js.map +1 -0
  73. package/dist/bundle.esm.js +4627 -1997
  74. package/dist/bundle.umd.js +4630 -1996
  75. package/dist/index.d.ts +5 -3
  76. package/dist/index.js +4 -2
  77. package/dist/index.js.map +1 -1
  78. package/dist/types/Renderer/copperRenderer.d.ts +1 -1
  79. package/dist/types/Renderer/copperRendererOnDemond.d.ts +1 -1
  80. package/dist/types/Scene/copperScene.d.ts +3 -0
  81. package/dist/types/Utils/segmentation/CommToolsData.d.ts +113 -11
  82. package/dist/types/Utils/segmentation/DragOperator.d.ts +13 -7
  83. package/dist/types/Utils/segmentation/DrawToolCore.d.ts +56 -76
  84. package/dist/types/Utils/segmentation/NrrdTools.d.ts +277 -15
  85. package/dist/types/Utils/segmentation/core/MaskVolume.d.ts +444 -0
  86. package/dist/types/Utils/segmentation/core/MigrationUtils.d.ts +97 -0
  87. package/dist/types/Utils/segmentation/core/UndoManager.d.ts +47 -0
  88. package/dist/types/Utils/segmentation/core/index.d.ts +76 -0
  89. package/dist/types/Utils/segmentation/core/types.d.ts +106 -0
  90. package/dist/types/Utils/segmentation/coreTools/coreType.d.ts +55 -39
  91. package/dist/types/Utils/segmentation/coreTools/gui.d.ts +10 -17
  92. package/dist/types/Utils/segmentation/eventRouter/EventRouter.d.ts +145 -0
  93. package/dist/types/Utils/segmentation/eventRouter/index.d.ts +7 -0
  94. package/dist/types/Utils/segmentation/eventRouter/types.d.ts +70 -0
  95. package/dist/types/Utils/segmentation/tools/BaseTool.d.ts +28 -0
  96. package/dist/types/Utils/segmentation/tools/ContrastTool.d.ts +26 -0
  97. package/dist/types/Utils/segmentation/tools/CrosshairTool.d.ts +30 -0
  98. package/dist/types/Utils/segmentation/tools/DragSliceTool.d.ts +44 -0
  99. package/dist/types/Utils/segmentation/tools/EraserTool.d.ts +16 -0
  100. package/dist/types/Utils/segmentation/tools/ImageStoreHelper.d.ts +62 -0
  101. package/dist/types/Utils/segmentation/tools/SphereTool.d.ts +36 -0
  102. package/dist/types/Utils/segmentation/tools/ZoomTool.d.ts +20 -0
  103. package/dist/types/Utils/segmentation/tools/index.d.ts +22 -0
  104. package/dist/types/index.d.ts +5 -3
  105. package/dist/types/types/types.d.ts +3 -3
  106. package/dist/types/types.d.ts +3 -3
  107. package/package.json +1 -1
  108. package/dist/Utils/segmentation/coreTools/archive.d.ts +0 -1
  109. package/dist/Utils/segmentation/coreTools/archive.js +0 -23
  110. package/dist/Utils/segmentation/coreTools/archive.js.map +0 -1
  111. package/dist/types/Utils/segmentation/coreTools/archive.d.ts +0 -1
@@ -1,9 +1,17 @@
1
1
  import { CommToolsData } from "./CommToolsData";
2
- import { switchEraserSize, switchPencilIcon, throttle } from "../utils";
2
+ import { switchEraserSize, switchPencilIcon } from "../utils";
3
+ import { EventRouter } from "./eventRouter";
4
+ import { SphereTool } from "./tools/SphereTool";
5
+ import { CrosshairTool } from "./tools/CrosshairTool";
6
+ import { ContrastTool } from "./tools/ContrastTool";
7
+ import { ZoomTool } from "./tools/ZoomTool";
8
+ import { EraserTool } from "./tools/EraserTool";
9
+ import { ImageStoreHelper } from "./tools/ImageStoreHelper";
10
+ import { UndoManager } from "./core";
3
11
  export class DrawToolCore extends CommToolsData {
4
- constructor(container) {
12
+ constructor(container, options) {
5
13
  const mainAreaContainer = document.createElement("div");
6
- super(container, mainAreaContainer);
14
+ super(container, mainAreaContainer, options);
7
15
  this.drawingPrameters = {
8
16
  handleOnDrawingMouseDown: (ev) => { },
9
17
  handleOnDrawingMouseMove: (ev) => { },
@@ -28,7 +36,13 @@ export class DrawToolCore extends CommToolsData {
28
36
  };
29
37
  this.eraserUrls = [];
30
38
  this.pencilUrls = [];
31
- this.undoArray = [];
39
+ 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;
32
46
  // need to return to parent
33
47
  this.start = () => { };
34
48
  /*************************************May consider to move outside *******************************************/
@@ -40,69 +54,137 @@ export class DrawToolCore extends CommToolsData {
40
54
  this.protectedData.ctxes.drawingCtx.stroke();
41
55
  };
42
56
  this.configContrastDragMode = () => {
43
- this.container.style.cursor = "pointer";
44
- this.container.addEventListener("pointerdown", this.contrastEventPrameters.handleOnContrastMouseDown, true);
45
- this.container.addEventListener("pointerup", this.contrastEventPrameters.handleOnContrastMouseUp, true);
57
+ this.contrastTool.configContrastDragMode();
46
58
  };
47
59
  this.removeContrastDragMode = () => {
48
- this.container.style.cursor = "";
49
- this.container.removeEventListener("pointerdown", this.contrastEventPrameters.handleOnContrastMouseDown, true);
50
- this.container.removeEventListener("pointermove", this.contrastEventPrameters.handleOnContrastMouseMove, true);
51
- this.container.removeEventListener("pointerup", this.contrastEventPrameters.handleOnContrastMouseUp, true);
52
- this.container.removeEventListener("pointerleave", this.contrastEventPrameters.handleOnContrastMouseLeave, true);
53
- this.setIsDrawFalse(1000);
60
+ this.contrastTool.removeContrastDragMode();
61
+ };
62
+ this.updateSlicesContrast = (value, flag) => {
63
+ this.contrastTool.updateSlicesContrast(value, flag);
64
+ };
65
+ this.repraintCurrentContrastSlice = () => {
66
+ this.contrastTool.repraintCurrentContrastSlice();
54
67
  };
55
68
  this.container = container;
56
69
  this.mainAreaContainer = mainAreaContainer;
70
+ this.initTools();
57
71
  this.initDrawToolCore();
58
72
  }
73
+ initTools() {
74
+ const toolCtx = {
75
+ nrrd_states: this.nrrd_states,
76
+ gui_states: this.gui_states,
77
+ protectedData: this.protectedData,
78
+ cursorPage: this.cursorPage,
79
+ };
80
+ this.imageStoreHelper = new ImageStoreHelper(toolCtx, {
81
+ setEmptyCanvasSize: (axis) => this.setEmptyCanvasSize(axis),
82
+ drawImageOnEmptyImage: (canvas) => this.drawImageOnEmptyImage(canvas),
83
+ });
84
+ this.sphereTool = new SphereTool(toolCtx, {
85
+ setEmptyCanvasSize: (axis) => this.setEmptyCanvasSize(axis),
86
+ drawImageOnEmptyImage: (canvas) => this.drawImageOnEmptyImage(canvas),
87
+ storeImageToAxis: (index, imageData, axis) => this.imageStoreHelper.storeImageToAxis(index, imageData, axis),
88
+ });
89
+ this.crosshairTool = new CrosshairTool(toolCtx);
90
+ this.contrastTool = new ContrastTool(toolCtx, this.container, this.contrastEventPrameters, {
91
+ setIsDrawFalse: (target) => this.setIsDrawFalse(target),
92
+ setSyncsliceNum: () => this.setSyncsliceNum(),
93
+ });
94
+ this.zoomTool = new ZoomTool(toolCtx, this.container, this.mainAreaContainer, {
95
+ resetPaintAreaUIPosition: (l, t) => this.resetPaintAreaUIPosition(l, t),
96
+ resizePaintArea: (moveDistance) => this.resizePaintArea(moveDistance),
97
+ setIsDrawFalse: (target) => this.setIsDrawFalse(target),
98
+ });
99
+ this.eraserTool = new EraserTool(toolCtx);
100
+ }
59
101
  initDrawToolCore() {
60
- let undoFlag = false;
61
- this.container.addEventListener("keydown", (ev) => {
62
- if (this.nrrd_states.configKeyBoard)
63
- return;
64
- if (ev.key === this.nrrd_states.keyboardSettings.draw && !this.gui_states.sphere && !this.gui_states.calculator) {
65
- if (this.protectedData.Is_Ctrl_Pressed) {
66
- this.protectedData.Is_Shift_Pressed = false;
67
- return;
102
+ // Initialize EventRouter for centralized event handling
103
+ this.eventRouter = new EventRouter({
104
+ container: this.container,
105
+ canvas: this.protectedData.canvases.drawingCanvas,
106
+ onModeChange: (prevMode, newMode) => {
107
+ // Use string comparison to avoid TypeScript narrowing issues
108
+ const prev = prevMode;
109
+ const next = newMode;
110
+ if (next === 'contrast') {
111
+ this.configContrastDragMode();
112
+ }
113
+ else if (prev === 'contrast') {
114
+ this.removeContrastDragMode();
115
+ this.gui_states.readyToUpdate = true;
116
+ }
117
+ if (next === 'crosshair') {
118
+ this.protectedData.Is_Draw = false;
68
119
  }
69
- this.protectedData.Is_Shift_Pressed = true;
70
- this.nrrd_states.enableCursorChoose = false;
71
- }
72
- if (ev.key === this.nrrd_states.keyboardSettings.crosshair) {
73
- this.protectedData.Is_Draw = false;
74
- this.nrrd_states.enableCursorChoose =
75
- !this.nrrd_states.enableCursorChoose;
76
120
  }
77
- if ((ev.ctrlKey || ev.metaKey) && ev.key === this.nrrd_states.keyboardSettings.undo) {
121
+ });
122
+ // Inject eventRouter into ToolContext so tools can query mode/state
123
+ const toolCtx = this.sphereTool['ctx'];
124
+ toolCtx.eventRouter = this.eventRouter;
125
+ // Configure keyboard settings from class field
126
+ this.eventRouter.setKeyboardSettings(this._keyboardSettings);
127
+ // Track undo flag for Ctrl+Z handling
128
+ let undoFlag = false;
129
+ // Register keyboard handlers with EventRouter
130
+ this.eventRouter.setKeydownHandler((ev) => {
131
+ var _a, _b;
132
+ if (this._configKeyBoard)
133
+ return;
134
+ // Handle undo (Ctrl+Z)
135
+ if ((ev.ctrlKey || ev.metaKey) && ev.key === this._keyboardSettings.undo) {
78
136
  undoFlag = true;
79
137
  this.undoLastPainting();
80
138
  }
139
+ // Handle redo (Ctrl+<redo key> or Ctrl+Shift+<undo key>)
140
+ const redoKey = this._keyboardSettings.redo;
141
+ const undoKeyUpper = this._keyboardSettings.undo.toUpperCase();
142
+ if ((ev.ctrlKey || ev.metaKey) && (ev.key === redoKey || (ev.shiftKey && ev.key === undoKeyUpper))) {
143
+ this.redoLastPainting();
144
+ }
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
+ }
150
+ }
151
+ // 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()) {
155
+ return; // Ctrl takes priority
156
+ }
157
+ // EventRouter will set mode to 'draw' via internal handler
158
+ }
81
159
  });
82
- this.container.addEventListener("keyup", (ev) => {
83
- if (this.nrrd_states.configKeyBoard)
160
+ this.eventRouter.setKeyupHandler((ev) => {
161
+ var _a, _b, _c, _d, _e, _f;
162
+ if (this._configKeyBoard)
84
163
  return;
85
- if (this.nrrd_states.keyboardSettings.contrast.includes(ev.key)) {
164
+ // Handle Ctrl key release (contrast mode toggle)
165
+ if (this._keyboardSettings.contrast.includes(ev.key)) {
86
166
  if (undoFlag) {
87
167
  this.gui_states.readyToUpdate = true;
88
168
  undoFlag = false;
89
169
  return;
90
170
  }
91
- // Ctrl key pressed on either Windows or macOS
92
- this.protectedData.Is_Shift_Pressed = false;
93
- this.protectedData.Is_Ctrl_Pressed = !this.protectedData.Is_Ctrl_Pressed;
94
- if (this.protectedData.Is_Ctrl_Pressed) {
95
- this.configContrastDragMode();
171
+ // Skip mode toggle when contrast shortcut is disabled
172
+ if (!((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.isContrastEnabled()))
173
+ 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')
176
+ return;
177
+ // 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');
96
180
  }
97
181
  else {
98
- this.removeContrastDragMode();
99
- this.gui_states.readyToUpdate = true;
182
+ (_f = this.eventRouter) === null || _f === void 0 ? void 0 : _f.setMode('idle');
100
183
  }
101
184
  }
102
- if (ev.key === this.nrrd_states.keyboardSettings.draw) {
103
- this.protectedData.Is_Shift_Pressed = false;
104
- }
105
185
  });
186
+ // Bind all event listeners
187
+ this.eventRouter.bindAll();
106
188
  }
107
189
  setEraserUrls(urls) {
108
190
  this.eraserUrls = urls;
@@ -114,31 +196,21 @@ export class DrawToolCore extends CommToolsData {
114
196
  this.gui_states.defaultPaintCursor;
115
197
  }
116
198
  setCurrentLayer() {
117
- let ctx;
118
- let canvas;
119
- switch (this.gui_states.label) {
120
- case "label1":
121
- ctx = this.protectedData.ctxes.drawingLayerOneCtx;
122
- canvas = this.protectedData.canvases.drawingCanvasLayerOne;
123
- break;
124
- case "label2":
125
- ctx = this.protectedData.ctxes.drawingLayerTwoCtx;
126
- canvas = this.protectedData.canvases.drawingCanvasLayerTwo;
127
- break;
128
- case "label3":
129
- ctx = this.protectedData.ctxes.drawingLayerThreeCtx;
130
- canvas = this.protectedData.canvases.drawingCanvasLayerThree;
131
- break;
132
- default:
133
- ctx = this.protectedData.ctxes.drawingLayerOneCtx;
134
- canvas = this.protectedData.canvases.drawingCanvasLayerOne;
135
- break;
199
+ const layer = this.gui_states.layer;
200
+ let target = this.protectedData.layerTargets.get(layer);
201
+ if (!target) {
202
+ // Fallback to first layer
203
+ const firstId = this.nrrd_states.layers[0];
204
+ target = this.protectedData.layerTargets.get(firstId);
136
205
  }
137
- return { ctx, canvas };
206
+ return { ctx: target.ctx, canvas: target.canvas };
138
207
  }
139
208
  draw(opts) {
140
209
  if (!!opts) {
141
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;
213
+ }
142
214
  this.nrrd_states.getSphere = opts === null || opts === void 0 ? void 0 : opts.getSphereData;
143
215
  this.nrrd_states.getCalculateSpherePositions = opts === null || opts === void 0 ? void 0 : opts.getCalculateSpherePositionsData;
144
216
  }
@@ -195,10 +267,7 @@ export class DrawToolCore extends CommToolsData {
195
267
  this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
196
268
  }
197
269
  clearSpherePrintStoreImages() {
198
- this.protectedData.maskData.paintImages.x.length = 0;
199
- this.protectedData.maskData.paintImages.y.length = 0;
200
- this.protectedData.maskData.paintImages.z.length = 0;
201
- this.createEmptyPaintImage(this.nrrd_states.dimensions, this.protectedData.maskData.paintImages);
270
+ this.sphereTool.clearSpherePrintStoreImages();
202
271
  }
203
272
  paintOnCanvas() {
204
273
  var _a, _b, _c;
@@ -225,13 +294,16 @@ export class DrawToolCore extends CommToolsData {
225
294
  this.protectedData.previousDrawingImage =
226
295
  this.protectedData.ctxes.drawingCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvas.width, this.protectedData.canvases.drawingCanvas.height);
227
296
  // let a global variable to store the wheel move event
228
- if (this.nrrd_states.keyboardSettings.mouseWheel === "Scroll:Zoom") {
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") {
229
300
  this.drawingPrameters.handleMouseZoomSliceWheel = this.configMouseZoomWheel();
230
301
  }
231
302
  else {
232
303
  this.drawingPrameters.handleMouseZoomSliceWheel = this.configMouseSliceWheel();
233
304
  }
234
- // init to add it
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)
235
307
  this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel, {
236
308
  passive: false,
237
309
  });
@@ -283,6 +355,7 @@ export class DrawToolCore extends CommToolsData {
283
355
  }
284
356
  };
285
357
  this.drawingPrameters.handleOnDrawingMouseDown = (e) => {
358
+ var _a, _b, _c, _d;
286
359
  if (leftclicked || rightclicked) {
287
360
  this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
288
361
  this.protectedData.ctxes.drawingLayerMasterCtx.closePath();
@@ -298,7 +371,7 @@ export class DrawToolCore extends CommToolsData {
298
371
  // remove it when mouse click down
299
372
  this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
300
373
  if (e.button === 0) {
301
- if (this.protectedData.Is_Shift_Pressed) {
374
+ if (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode()) === 'draw') {
302
375
  leftclicked = true;
303
376
  lines = [];
304
377
  Is_Painting = true;
@@ -318,10 +391,20 @@ export class DrawToolCore extends CommToolsData {
318
391
  }
319
392
  this.nrrd_states.drawStartPos.x = e.offsetX;
320
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
+ }
321
404
  this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
322
405
  this.protectedData.canvases.drawingCanvas.addEventListener("pointermove", this.drawingPrameters.handleOnDrawingMouseMove);
323
406
  }
324
- else if (this.nrrd_states.enableCursorChoose) {
407
+ else if ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled()) {
325
408
  this.nrrd_states.cursorPageX =
326
409
  e.offsetX / this.nrrd_states.sizeFoctor;
327
410
  this.nrrd_states.cursorPageY =
@@ -329,10 +412,10 @@ export class DrawToolCore extends CommToolsData {
329
412
  this.enableCrosshair();
330
413
  this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
331
414
  }
332
- else if (this.gui_states.sphere && !this.nrrd_states.enableCursorChoose) {
415
+ else if (this.gui_states.sphere && !((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.isCrosshairEnabled())) {
333
416
  sphere(e);
334
417
  }
335
- else if (this.gui_states.calculator && !this.nrrd_states.enableCursorChoose) {
418
+ else if (this.gui_states.calculator && !((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.isCrosshairEnabled())) {
336
419
  this.drawCalSphereDown(e.offsetX, e.offsetY, this.nrrd_states.currentIndex, this.gui_states.cal_distance);
337
420
  }
338
421
  }
@@ -350,8 +433,17 @@ export class DrawToolCore extends CommToolsData {
350
433
  return;
351
434
  }
352
435
  };
353
- // disable browser right click menu
354
- this.protectedData.canvases.drawingCanvas.addEventListener("pointerdown", this.drawingPrameters.handleOnDrawingMouseDown, true);
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
+ }
355
447
  const sphere = (e) => {
356
448
  // set sphere size
357
449
  this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
@@ -372,48 +464,36 @@ export class DrawToolCore extends CommToolsData {
372
464
  this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleSphereWheel, true);
373
465
  this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
374
466
  };
375
- const redrawPreviousImageToLabelCtx = (ctx, label = "default") => {
467
+ const redrawPreviousImageToLayerCtx = (ctx) => {
376
468
  var _a;
377
- let paintImages;
378
- switch (label) {
379
- case "label1":
380
- paintImages = this.protectedData.maskData.paintImagesLabel1;
381
- break;
382
- case "label2":
383
- paintImages = this.protectedData.maskData.paintImagesLabel2;
384
- break;
385
- case "label3":
386
- paintImages = this.protectedData.maskData.paintImagesLabel3;
387
- break;
388
- default:
389
- paintImages = this.protectedData.maskData.paintImages;
390
- break;
391
- }
392
- const tempPreImg = (_a = this.filterDrawedImage(this.protectedData.axis, this.nrrd_states.currentIndex, paintImages)) === null || _a === void 0 ? void 0 : _a.image;
469
+ const tempPreImg = (_a = this.filterDrawedImage(this.protectedData.axis, this.nrrd_states.currentIndex)) === null || _a === void 0 ? void 0 : _a.image;
393
470
  this.protectedData.canvases.emptyCanvas.width =
394
471
  this.protectedData.canvases.emptyCanvas.width;
395
- if (tempPreImg && label == "default") {
472
+ if (tempPreImg) {
396
473
  this.protectedData.previousDrawingImage = tempPreImg;
397
474
  }
398
475
  this.protectedData.ctxes.emptyCtx.putImageData(tempPreImg, 0, 0);
399
- // draw privous image
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;
400
480
  ctx.drawImage(this.protectedData.canvases.emptyCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
401
481
  };
402
482
  this.drawingPrameters.handleOnDrawingMouseUp = (e) => {
483
+ var _a, _b, _c, _d;
403
484
  if (e.button === 0) {
404
- if (this.protectedData.Is_Shift_Pressed || Is_Painting) {
485
+ if (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode()) === 'draw' || Is_Painting) {
405
486
  leftclicked = false;
406
487
  let { ctx, canvas } = this.setCurrentLayer();
407
488
  ctx.closePath();
408
489
  this.protectedData.canvases.drawingCanvas.removeEventListener("pointermove", this.drawingPrameters.handleOnDrawingMouseMove);
409
490
  if (!this.gui_states.Eraser) {
410
- if (this.gui_states.segmentation) {
411
- this.protectedData.canvases.drawingCanvasLayerMaster.width =
412
- this.protectedData.canvases.drawingCanvasLayerMaster.width;
491
+ if (this.gui_states.pencil) {
492
+ // Clear only the current layer canvas (NOT master)
413
493
  canvas.width = canvas.width;
414
- redrawPreviousImageToLabelCtx(this.protectedData.ctxes.drawingLayerMasterCtx);
415
- redrawPreviousImageToLabelCtx(ctx, this.gui_states.label);
416
- // draw new drawings
494
+ // Redraw previous layer data from volume
495
+ redrawPreviousImageToLayerCtx(ctx);
496
+ // Draw new pencil strokes on current layer canvas
417
497
  ctx.beginPath();
418
498
  ctx.moveTo(lines[0].x, lines[0].y);
419
499
  for (let i = 1; i < lines.length; i++) {
@@ -423,36 +503,37 @@ export class DrawToolCore extends CommToolsData {
423
503
  ctx.lineWidth = 1;
424
504
  ctx.fillStyle = this.gui_states.fillColor;
425
505
  ctx.fill();
426
- // draw layer to master layer
427
- this.protectedData.ctxes.drawingLayerMasterCtx.drawImage(canvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
506
+ // Composite ALL layers to master (not just current layer)
507
+ this.compositeAllLayers();
428
508
  }
429
509
  }
430
510
  this.protectedData.previousDrawingImage =
431
511
  this.protectedData.ctxes.drawingLayerMasterCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvasLayerMaster.width, this.protectedData.canvases.drawingCanvasLayerMaster.height);
432
- this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.label);
512
+ this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.layer);
433
513
  if (this.gui_states.Eraser) {
434
- const restLabels = this.getRestLabel();
435
- this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[0]);
436
- this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[1]);
514
+ const restLayers = this.getRestLayer();
515
+ this.storeEachLayerImage(this.nrrd_states.currentIndex, restLayers[0]);
516
+ this.storeEachLayerImage(this.nrrd_states.currentIndex, restLayers[1]);
437
517
  }
438
518
  Is_Painting = false;
439
- /**
440
- * store undo array
441
- */
442
- const currentUndoObj = this.getCurrentUndo();
443
- const src = this.protectedData.canvases.drawingCanvasLayerMaster.toDataURL();
444
- const image = new Image();
445
- image.src = src;
446
- if (currentUndoObj.length > 0) {
447
- currentUndoObj[0].layers[this.gui_states.label].push(image);
448
- }
449
- else {
450
- const undoObj = {
451
- sliceIndex: this.nrrd_states.currentIndex,
452
- layers: { label1: [], label2: [], label3: [] },
453
- };
454
- undoObj.layers[this.gui_states.label].push(image);
455
- this.undoArray.push(undoObj);
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;
456
537
  }
457
538
  // add wheel after pointer up
458
539
  this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel, {
@@ -460,7 +541,7 @@ export class DrawToolCore extends CommToolsData {
460
541
  });
461
542
  }
462
543
  else if (this.gui_states.sphere &&
463
- !this.nrrd_states.enableCursorChoose) {
544
+ !((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled())) {
464
545
  // plan B
465
546
  // findout all index in the sphere radius range in Axial view
466
547
  if (this.nrrd_states.spherePlanB) {
@@ -480,12 +561,12 @@ export class DrawToolCore extends CommToolsData {
480
561
  this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
481
562
  }
482
563
  else if ((this.gui_states.sphere || this.gui_states.calculator) &&
483
- this.nrrd_states.enableCursorChoose) {
564
+ ((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.isCrosshairEnabled())) {
484
565
  this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
485
566
  this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
486
567
  }
487
568
  else if (this.gui_states.calculator &&
488
- !this.nrrd_states.enableCursorChoose) {
569
+ !((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.isCrosshairEnabled())) {
489
570
  // When mouse up
490
571
  this.drawCalSphereUp();
491
572
  }
@@ -504,13 +585,12 @@ export class DrawToolCore extends CommToolsData {
504
585
  else {
505
586
  return;
506
587
  }
507
- if (!this.gui_states.segmentation) {
588
+ if (!this.gui_states.pencil) {
508
589
  this.setIsDrawFalse(100);
509
590
  }
510
591
  };
511
592
  this.protectedData.canvases.drawingCanvas.addEventListener("pointerleave", (e) => {
512
593
  Is_Painting = false;
513
- // (this.sceneIn as copperScene).controls.enabled = true;
514
594
  if (leftclicked) {
515
595
  leftclicked = false;
516
596
  this.protectedData.ctxes.drawingLayerMasterCtx.closePath();
@@ -523,11 +603,12 @@ export class DrawToolCore extends CommToolsData {
523
603
  this.protectedData.canvases.drawingCanvas.removeEventListener("pointermove", this.drawingPrameters.handleOnPanMouseMove);
524
604
  }
525
605
  this.setIsDrawFalse(100);
526
- if (this.gui_states.segmentation) {
606
+ if (this.gui_states.pencil) {
527
607
  this.setIsDrawFalse(1000);
528
608
  }
529
609
  });
530
610
  this.start = () => {
611
+ var _a, _b;
531
612
  if (this.gui_states.readyToUpdate) {
532
613
  this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
533
614
  this.protectedData.ctxes.drawingCtx.globalAlpha =
@@ -535,16 +616,17 @@ export class DrawToolCore extends CommToolsData {
535
616
  if (this.protectedData.Is_Draw) {
536
617
  this.protectedData.ctxes.drawingLayerMasterCtx.lineCap = "round";
537
618
  this.protectedData.ctxes.drawingLayerMasterCtx.globalAlpha = 1;
538
- this.protectedData.ctxes.drawingLayerOneCtx.lineCap = "round";
539
- this.protectedData.ctxes.drawingLayerOneCtx.globalAlpha = 1;
540
- this.protectedData.ctxes.drawingLayerTwoCtx.lineCap = "round";
541
- this.protectedData.ctxes.drawingLayerTwoCtx.globalAlpha = 1;
542
- this.protectedData.ctxes.drawingLayerThreeCtx.lineCap = "round";
543
- this.protectedData.ctxes.drawingLayerThreeCtx.globalAlpha = 1;
619
+ for (const [, target] of this.protectedData.layerTargets) {
620
+ target.ctx.lineCap = "round";
621
+ target.ctx.globalAlpha = 1;
622
+ }
544
623
  }
545
624
  else {
546
- if (this.protectedData.Is_Shift_Pressed) {
547
- if (!this.gui_states.segmentation &&
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();
627
+ if (currentMode === 'draw') {
628
+ // Draw mode: show brush circle preview
629
+ if (!this.gui_states.pencil &&
548
630
  !this.gui_states.Eraser &&
549
631
  this.nrrd_states.Mouse_Over) {
550
632
  this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
@@ -557,7 +639,8 @@ export class DrawToolCore extends CommToolsData {
557
639
  this.protectedData.ctxes.drawingCtx.stroke();
558
640
  }
559
641
  }
560
- if (this.nrrd_states.enableCursorChoose) {
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)
561
644
  this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
562
645
  const ex = this.nrrd_states.cursorPageX * this.nrrd_states.sizeFoctor;
563
646
  const ey = this.nrrd_states.cursorPageY * this.nrrd_states.sizeFoctor;
@@ -565,6 +648,8 @@ export class DrawToolCore extends CommToolsData {
565
648
  this.drawLine(0, ey, this.nrrd_states.changedHeight, ey);
566
649
  }
567
650
  }
651
+ // globalAlpha was set to gui_states.globalAlpha at the top of start().
652
+ // Master stores full-alpha pixels; transparency applied here only.
568
653
  this.protectedData.ctxes.drawingCtx.drawImage(this.protectedData.canvases.drawingCanvasLayerMaster, 0, 0);
569
654
  }
570
655
  else {
@@ -575,7 +660,7 @@ export class DrawToolCore extends CommToolsData {
575
660
  drawLinesOnLayer(ctx, x, y) {
576
661
  ctx.beginPath();
577
662
  ctx.moveTo(this.nrrd_states.drawStartPos.x, this.nrrd_states.drawStartPos.y);
578
- if (this.gui_states.segmentation) {
663
+ if (this.gui_states.pencil) {
579
664
  ctx.strokeStyle = this.gui_states.color;
580
665
  ctx.lineWidth = this.gui_states.lineWidth;
581
666
  }
@@ -589,8 +674,10 @@ export class DrawToolCore extends CommToolsData {
589
674
  }
590
675
  paintOnCanvasLayer(x, y) {
591
676
  let { ctx, canvas } = this.setCurrentLayer();
677
+ // Draw only on the current layer canvas (not master directly)
592
678
  this.drawLinesOnLayer(ctx, x, y);
593
- this.drawLinesOnLayer(this.protectedData.ctxes.drawingLayerMasterCtx, x, y);
679
+ // Composite all layers to master to preserve other layers' data
680
+ this.compositeAllLayers();
594
681
  // reset drawing start position to current position.
595
682
  this.nrrd_states.drawStartPos.x = x;
596
683
  this.nrrd_states.drawStartPos.y = y;
@@ -624,15 +711,13 @@ export class DrawToolCore extends CommToolsData {
624
711
  * it should be hide, so we don't need to add it to mainAreaContainer
625
712
  */
626
713
  this.protectedData.canvases.drawingCanvasLayerMaster.width =
627
- this.protectedData.canvases.drawingCanvasLayerOne.width =
628
- this.protectedData.canvases.drawingCanvasLayerTwo.width =
629
- this.protectedData.canvases.drawingCanvasLayerThree.width =
630
- this.nrrd_states.changedWidth;
714
+ this.nrrd_states.changedWidth;
631
715
  this.protectedData.canvases.drawingCanvasLayerMaster.height =
632
- this.protectedData.canvases.drawingCanvasLayerOne.height =
633
- this.protectedData.canvases.drawingCanvasLayerTwo.height =
634
- this.protectedData.canvases.drawingCanvasLayerThree.height =
635
- this.nrrd_states.changedHeight;
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;
720
+ }
636
721
  /**
637
722
  * display and drawing canvas container
638
723
  */
@@ -646,67 +731,11 @@ export class DrawToolCore extends CommToolsData {
646
731
  this.mainAreaContainer.appendChild(this.protectedData.canvases.drawingCanvas);
647
732
  }
648
733
  useEraser() {
649
- const clearArc = (x, y, radius) => {
650
- var calcWidth = radius - this.nrrd_states.stepClear;
651
- var calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth);
652
- var posX = x - calcWidth;
653
- var posY = y - calcHeight;
654
- var widthX = 2 * calcWidth;
655
- var heightY = 2 * calcHeight;
656
- if (this.nrrd_states.stepClear <= radius) {
657
- this.protectedData.ctxes.drawingLayerMasterCtx.clearRect(posX, posY, widthX, heightY);
658
- this.protectedData.ctxes.drawingLayerOneCtx.clearRect(posX, posY, widthX, heightY);
659
- this.protectedData.ctxes.drawingLayerTwoCtx.clearRect(posX, posY, widthX, heightY);
660
- this.protectedData.ctxes.drawingLayerThreeCtx.clearRect(posX, posY, widthX, heightY);
661
- this.nrrd_states.stepClear += 1;
662
- clearArc(x, y, radius);
663
- }
664
- };
665
- return clearArc;
734
+ return this.eraserTool.createClearArc();
666
735
  }
667
736
  // drawing canvas mouse zoom wheel
668
737
  configMouseZoomWheel() {
669
- let moveDistance = 1;
670
- const handleMouseZoomSliceWheelMove = (e) => {
671
- if (this.protectedData.Is_Shift_Pressed) {
672
- return;
673
- }
674
- e.preventDefault();
675
- // this.nrrd_states.originWidth;
676
- const delta = e.detail ? e.detail > 0 : e.wheelDelta < 0;
677
- this.protectedData.Is_Draw = true;
678
- var rect = this.container.getBoundingClientRect();
679
- const ratioL = (e.clientX - rect.left -
680
- this.mainAreaContainer.offsetLeft -
681
- this.protectedData.canvases.drawingCanvas.offsetLeft) /
682
- this.protectedData.canvases.drawingCanvas.offsetWidth;
683
- const ratioT = (e.clientY - rect.top -
684
- this.mainAreaContainer.offsetTop -
685
- this.protectedData.canvases.drawingCanvas.offsetTop) /
686
- this.protectedData.canvases.drawingCanvas.offsetHeight;
687
- const ratioDelta = !delta ? 1 + 0.1 : 1 - 0.1;
688
- const w = this.protectedData.canvases.drawingCanvas.offsetWidth * ratioDelta;
689
- const h = this.protectedData.canvases.drawingCanvas.offsetHeight * ratioDelta;
690
- const l = Math.round(e.clientX - this.mainAreaContainer.offsetLeft - w * ratioL - rect.left);
691
- const t = Math.round(e.clientY - this.mainAreaContainer.offsetTop - h * ratioT - rect.top);
692
- moveDistance = w / this.nrrd_states.originWidth;
693
- if (moveDistance > 8) {
694
- moveDistance = 8;
695
- // this.resetPaintAreaUIPosition();
696
- }
697
- else if (moveDistance < 1) {
698
- moveDistance = 1;
699
- this.resetPaintAreaUIPosition();
700
- this.resizePaintArea(moveDistance);
701
- }
702
- else {
703
- this.resetPaintAreaUIPosition(l, t);
704
- this.resizePaintArea(moveDistance);
705
- }
706
- this.setIsDrawFalse(1000);
707
- this.nrrd_states.sizeFoctor = moveDistance;
708
- };
709
- return handleMouseZoomSliceWheelMove;
738
+ return this.zoomTool.configMouseZoomWheel();
710
739
  }
711
740
  configMouseSliceWheel() {
712
741
  /**
@@ -716,742 +745,205 @@ export class DrawToolCore extends CommToolsData {
716
745
  throw new Error("Child class must implement abstract redrawDisplayCanvas, currently you can find it in NrrdTools.");
717
746
  }
718
747
  enableCrosshair() {
719
- this.nrrd_states.isCursorSelect = true;
720
- switch (this.protectedData.axis) {
721
- case "x":
722
- this.cursorPage.x.updated = true;
723
- this.cursorPage.y.updated = false;
724
- this.cursorPage.z.updated = false;
725
- break;
726
- case "y":
727
- this.cursorPage.x.updated = false;
728
- this.cursorPage.y.updated = true;
729
- this.cursorPage.z.updated = false;
730
- break;
731
- case "z":
732
- this.cursorPage.x.updated = false;
733
- this.cursorPage.y.updated = false;
734
- this.cursorPage.z.updated = true;
735
- break;
736
- }
748
+ this.crosshairTool.enableCrosshair();
737
749
  }
738
750
  drawImageOnEmptyImage(canvas) {
739
- this.protectedData.ctxes.emptyCtx.drawImage(canvas, 0, 0, this.protectedData.canvases.emptyCanvas.width, this.protectedData.canvases.emptyCanvas.height);
740
- }
741
- /****************************Sphere calculate distance functions****************************************************/
742
- getSpherePosition(position, axis) {
743
- const mouseX = position[axis][0];
744
- const mouseY = position[axis][1];
745
- const sliceIndex = position[axis][2];
746
- return { x: mouseX, y: mouseY, z: sliceIndex };
747
- }
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).
758
+ ctx.imageSmoothingEnabled = false;
759
+ ctx.drawImage(canvas, 0, 0, w, h);
760
+ }
761
+ /****************************Sphere functions (delegated to SphereTool)****************************************************/
748
762
  drawCalculatorSphereOnEachViews(axis) {
749
- // init sphere canvas width and height
750
- this.setSphereCanvasSize(axis);
751
- // get drawingSphere canvas for storing
752
- const ctx = this.protectedData.ctxes.drawingSphereCtx;
753
- const canvas = this.protectedData.canvases.drawingSphereCanvas;
754
- let tumourPosition = !!this.nrrd_states.tumourSphereOrigin ? Object.assign(this.getSpherePosition(this.nrrd_states.tumourSphereOrigin, axis), { color: this.nrrd_states.tumourColor }) : null;
755
- let skinPosition = !!this.nrrd_states.skinSphereOrigin ? Object.assign(this.getSpherePosition(this.nrrd_states.skinSphereOrigin, axis), { color: this.nrrd_states.skinColor }) : null;
756
- let ribcagePosition = !!this.nrrd_states.ribSphereOrigin ? Object.assign(this.getSpherePosition(this.nrrd_states.ribSphereOrigin, axis), { color: this.nrrd_states.ribcageColor }) : null;
757
- let nipplePosition = !!this.nrrd_states.nippleSphereOrigin ? Object.assign(this.getSpherePosition(this.nrrd_states.nippleSphereOrigin, axis), { color: this.nrrd_states.nippleColor }) : null;
758
- let positionGroup = [];
759
- if (!!tumourPosition)
760
- positionGroup.push(tumourPosition);
761
- if (!!skinPosition)
762
- positionGroup.push(skinPosition);
763
- if (!!ribcagePosition)
764
- positionGroup.push(ribcagePosition);
765
- if (!!nipplePosition)
766
- positionGroup.push(nipplePosition);
767
- let copyPosition = JSON.parse(JSON.stringify(positionGroup));
768
- let rePositionGroup = [];
769
- // group same slice points
770
- positionGroup.forEach((p) => {
771
- let temp = [];
772
- let sameIndex = [];
773
- for (let i = 0; i < copyPosition.length; i++) {
774
- if (p.z == copyPosition[i].z) {
775
- temp.push(copyPosition[i]);
776
- sameIndex.push(i);
777
- }
778
- }
779
- sameIndex.reverse();
780
- sameIndex.forEach(i => copyPosition.splice(i, 1));
781
- if (temp.length > 0)
782
- rePositionGroup.push(temp);
783
- if (copyPosition.length == 0) {
784
- return;
785
- }
786
- });
787
- rePositionGroup.forEach((group) => {
788
- group.forEach(p => {
789
- this.drawSphereCore(ctx, p.x, p.y, this.nrrd_states.sphereRadius, p.color);
790
- });
791
- this.storeSphereImages(group[0].z, axis);
792
- ctx.clearRect(0, 0, canvas.width, canvas.height);
793
- });
794
- }
795
- /****************************Sphere functions****************************************************/
796
- // for sphere
797
- storeSphereImages(index, axis) {
798
- this.setEmptyCanvasSize(axis);
799
- this.drawImageOnEmptyImage(this.protectedData.canvases.drawingSphereCanvas);
800
- let imageData = this.protectedData.ctxes.emptyCtx.getImageData(0, 0, this.protectedData.canvases.emptyCanvas.width, this.protectedData.canvases.emptyCanvas.height);
801
- this.storeImageToAxis(index, this.protectedData.maskData.paintImages, imageData, axis);
763
+ this.sphereTool.drawCalculatorSphereOnEachViews(axis);
802
764
  }
803
765
  drawSphereOnEachViews(decay, axis) {
804
- // init sphere canvas width and height
805
- this.setSphereCanvasSize(axis);
806
- const mouseX = this.nrrd_states.sphereOrigin[axis][0];
807
- const mouseY = this.nrrd_states.sphereOrigin[axis][1];
808
- const originIndex = this.nrrd_states.sphereOrigin[axis][2];
809
- const preIndex = originIndex - decay;
810
- const nextIndex = originIndex + decay;
811
- const ctx = this.protectedData.ctxes.drawingSphereCtx;
812
- const canvas = this.protectedData.canvases.drawingSphereCanvas;
813
- if (preIndex === nextIndex) {
814
- this.drawSphereCore(ctx, mouseX, mouseY, this.nrrd_states.sphereRadius, this.gui_states.fillColor);
815
- this.storeSphereImages(preIndex, axis);
816
- }
817
- else {
818
- this.drawSphereCore(ctx, mouseX, mouseY, (this.nrrd_states.sphereRadius - decay), this.gui_states.fillColor);
819
- this.drawImageOnEmptyImage(canvas);
820
- this.storeSphereImages(preIndex, axis);
821
- this.storeSphereImages(nextIndex, axis);
822
- }
823
- ctx.clearRect(0, 0, canvas.width, canvas.height);
766
+ this.sphereTool.drawSphereOnEachViews(decay, axis);
824
767
  }
825
- /**
826
- *
827
- * @param ctx draw sphere canvas ctx
828
- * @param x width must be match the origin size, size factor 1, ignore the size factor
829
- * @param y height must be match the origin size, size factor 1, ignore the size factor
830
- * @param radius radius must be match the origin size, size factor 1, ignore the size factor
831
- * @param color sphere color
832
- */
833
- drawSphereCore(ctx, x, y, radius, color) {
834
- ctx.beginPath();
835
- ctx.arc(x, y, radius, 0, 2 * Math.PI);
836
- ctx.fillStyle = color;
837
- ctx.fill();
838
- ctx.closePath();
839
- }
840
- setSphereCanvasSize(axis) {
841
- switch (!!axis ? axis : this.protectedData.axis) {
842
- case "x":
843
- this.protectedData.canvases.drawingSphereCanvas.width =
844
- this.nrrd_states.nrrd_z_mm;
845
- this.protectedData.canvases.drawingSphereCanvas.height =
846
- this.nrrd_states.nrrd_y_mm;
847
- break;
848
- case "y":
849
- this.protectedData.canvases.drawingSphereCanvas.width =
850
- this.nrrd_states.nrrd_x_mm;
851
- this.protectedData.canvases.drawingSphereCanvas.height =
852
- this.nrrd_states.nrrd_z_mm;
853
- break;
854
- case "z":
855
- this.protectedData.canvases.drawingSphereCanvas.width =
856
- this.nrrd_states.nrrd_x_mm;
857
- this.protectedData.canvases.drawingSphereCanvas.height =
858
- this.nrrd_states.nrrd_y_mm;
859
- break;
860
- }
861
- }
862
- // drawing canvas mouse shpere wheel
863
768
  configMouseSphereWheel() {
864
- const sphereEvent = (e) => {
865
- e.preventDefault();
866
- if (e.deltaY < 0) {
867
- this.nrrd_states.sphereRadius += 1;
868
- }
869
- else {
870
- this.nrrd_states.sphereRadius -= 1;
871
- }
872
- // limited the radius max and min
873
- this.nrrd_states.sphereRadius = Math.max(1, Math.min(this.nrrd_states.sphereRadius, 50));
874
- // get mouse position
875
- const mouseX = this.nrrd_states.sphereOrigin[this.protectedData.axis][0];
876
- const mouseY = this.nrrd_states.sphereOrigin[this.protectedData.axis][1];
877
- this.drawSphere(mouseX, mouseY, this.nrrd_states.sphereRadius);
878
- };
879
- return sphereEvent;
769
+ return this.sphereTool.configMouseSphereWheel();
880
770
  }
881
771
  drawCalculatorSphere(radius) {
882
- // clear canvas
883
- const [canvas, ctx] = this.clearSphereCanvas();
884
- if (!!this.nrrd_states.tumourSphereOrigin && this.nrrd_states.tumourSphereOrigin[this.protectedData.axis][2] === this.nrrd_states.currentIndex) {
885
- this.drawSphereCore(ctx, this.nrrd_states.tumourSphereOrigin[this.protectedData.axis][0], this.nrrd_states.tumourSphereOrigin[this.protectedData.axis][1], radius, this.nrrd_states.tumourColor);
886
- }
887
- if (!!this.nrrd_states.skinSphereOrigin && this.nrrd_states.skinSphereOrigin[this.protectedData.axis][2] === this.nrrd_states.currentIndex) {
888
- this.drawSphereCore(ctx, this.nrrd_states.skinSphereOrigin[this.protectedData.axis][0], this.nrrd_states.skinSphereOrigin[this.protectedData.axis][1], radius, this.nrrd_states.skinColor);
889
- }
890
- if (!!this.nrrd_states.ribSphereOrigin && this.nrrd_states.ribSphereOrigin[this.protectedData.axis][2] === this.nrrd_states.currentIndex) {
891
- this.drawSphereCore(ctx, this.nrrd_states.ribSphereOrigin[this.protectedData.axis][0], this.nrrd_states.ribSphereOrigin[this.protectedData.axis][1], radius, this.nrrd_states.ribcageColor);
892
- }
893
- if (!!this.nrrd_states.nippleSphereOrigin && this.nrrd_states.nippleSphereOrigin[this.protectedData.axis][2] === this.nrrd_states.currentIndex) {
894
- this.drawSphereCore(ctx, this.nrrd_states.nippleSphereOrigin[this.protectedData.axis][0], this.nrrd_states.nippleSphereOrigin[this.protectedData.axis][1], radius, this.nrrd_states.nippleColor);
895
- }
896
- this.protectedData.ctxes.drawingLayerMasterCtx.drawImage(canvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
772
+ this.sphereTool.drawCalculatorSphere(radius);
897
773
  }
898
774
  drawSphere(mouseX, mouseY, radius) {
899
- console.log(radius);
900
- // clear canvas
901
- const [canvas, ctx] = this.clearSphereCanvas();
902
- this.drawSphereCore(ctx, mouseX, mouseY, radius, this.gui_states.fillColor);
903
- console.log("when drawing shpere on canvas");
904
- console.log("Line 1256 mastercanvasesize:", this.protectedData.canvases.drawingCanvasLayerMaster.width, this.protectedData.canvases.drawingCanvasLayerMaster.height);
905
- console.log("current xy:", mouseX, mouseY);
906
- console.log("current changed width:", this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
907
- this.protectedData.ctxes.drawingLayerMasterCtx.drawImage(canvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
908
- }
909
- clearSphereCanvas() {
910
- // this.protectedData.canvases.drawingSphereCanvas.width =
911
- // this.protectedData.canvases.drawingCanvasLayerMaster.width;
912
- // this.protectedData.canvases.drawingSphereCanvas.height =
913
- // this.protectedData.canvases.drawingCanvasLayerMaster.height;
914
- // clear drawingCanvasLayerMaster
915
- this.protectedData.canvases.drawingCanvasLayerMaster.width = this.protectedData.canvases.drawingCanvasLayerMaster.width;
916
- // resize sphere canvas size to original size
917
- this.protectedData.canvases.drawingSphereCanvas.width = this.protectedData.canvases.originCanvas.width;
918
- this.protectedData.canvases.drawingSphereCanvas.height = this.protectedData.canvases.originCanvas.height;
919
- const canvas = this.protectedData.canvases.drawingSphereCanvas;
920
- const ctx = this.protectedData.ctxes.drawingSphereCtx;
921
- return [canvas, ctx];
775
+ this.sphereTool.drawSphere(mouseX, mouseY, radius);
922
776
  }
923
777
  /**
924
- * We generate the MRI slice from threejs based on mm, but when we display it is based on pixel size/distance.
925
- * So, the index munber on each axis (sagittal, axial, coronal) is the slice's depth in mm distance. And the width and height displayed on screen is the slice's width and height in pixel distance.
926
- *
927
- * When we switch into different axis' views, we need to convert current view's the depth to the pixel distance in other views width or height, and convert the current view's width or height from pixel distance to mm distance as other views' depth (slice index) in general.
928
- *
929
- * Then as for the crosshair (Cursor Inspector), we also need to convert the cursor point (x, y, z) to other views' (x, y, z).
930
- *
931
- * @param from "x" | "y" | "z", current view axis, "x: sagittle, y: coronal, z: axial".
932
- * @param to "x" | "y" | "z", target view axis (where you want jump to), "x: sagittle, y: coronal, z: axial".
933
- * @param cursorNumX number, cursor point x on current axis's slice. (pixel distance)
934
- * @param cursorNumY number, cursor point y on current axis's slice. (pixel distance)
935
- * @param currentSliceIndex number, current axis's slice's index/depth. (mm distance)
936
- * @returns
937
- */
778
+ * Convert cursor point between axis views.
779
+ * Delegated to CrosshairTool.
780
+ */
938
781
  convertCursorPoint(from, to, cursorNumX, cursorNumY, currentSliceIndex) {
939
- const nrrd = this.nrrd_states;
940
- const dimensions = nrrd.dimensions;
941
- const ratios = nrrd.ratios;
942
- const { nrrd_x_mm, nrrd_y_mm, nrrd_z_mm } = nrrd;
943
- let currentIndex = 0;
944
- let oldIndex = 0;
945
- let convertCursorNumX = 0;
946
- let convertCursorNumY = 0;
947
- const convertIndex = {
948
- x: {
949
- y: (val) => Math.ceil((val / nrrd_x_mm) * dimensions[0]),
950
- z: (val) => Math.ceil((val / nrrd_z_mm) * dimensions[2]),
951
- },
952
- y: {
953
- x: (val) => Math.ceil((val / nrrd_y_mm) * dimensions[1]),
954
- z: (val) => Math.ceil((1 - val / nrrd_z_mm) * dimensions[2]),
955
- },
956
- z: {
957
- x: (val) => Math.ceil((val / nrrd_x_mm) * dimensions[0]),
958
- y: (val) => Math.ceil((val / nrrd_y_mm) * dimensions[1]),
959
- },
960
- };
961
- const convertCursor = {
962
- x: {
963
- y: (sliceIndex) => Math.ceil((sliceIndex / dimensions[0]) * nrrd_x_mm),
964
- z: (sliceIndex) => Math.ceil((sliceIndex / dimensions[0]) * nrrd_x_mm),
965
- },
966
- y: {
967
- x: (sliceIndex) => Math.ceil((sliceIndex / dimensions[1]) * nrrd_y_mm),
968
- z: (sliceIndex) => Math.ceil((sliceIndex / dimensions[1]) * nrrd_y_mm),
969
- },
970
- z: {
971
- x: (sliceIndex) => Math.ceil((sliceIndex / dimensions[2]) * nrrd_z_mm),
972
- y: (sliceIndex) => Math.ceil((1 - sliceIndex / dimensions[2]) * nrrd_z_mm),
973
- },
974
- };
975
- if (from === to) {
976
- return;
977
- }
978
- if (from === "z" && to === "x") {
979
- currentIndex = convertIndex[from][to](cursorNumX);
980
- oldIndex = currentIndex * ratios[to];
981
- convertCursorNumX = convertCursor[from][to](currentSliceIndex);
982
- convertCursorNumY = cursorNumY;
983
- }
984
- else if (from === "y" && to === "x") {
985
- currentIndex = convertIndex[from][to](cursorNumX);
986
- oldIndex = currentIndex * ratios.x;
987
- convertCursorNumY = convertCursor[from][to](currentSliceIndex);
988
- convertCursorNumX = dimensions[2] * ratios.z - cursorNumY;
989
- }
990
- else if (from === "z" && to === "y") {
991
- currentIndex = convertIndex[from][to](cursorNumY);
992
- oldIndex = currentIndex * ratios[to];
993
- convertCursorNumY = convertCursor[from][to](currentSliceIndex);
994
- convertCursorNumX = cursorNumX;
995
- }
996
- else if (from === "x" && to === "y") {
997
- currentIndex = convertIndex[from][to](cursorNumY);
998
- oldIndex = currentIndex * ratios[to];
999
- convertCursorNumX = convertCursor[from][to](currentSliceIndex);
1000
- convertCursorNumY = dimensions[2] * ratios.z - cursorNumX;
1001
- }
1002
- else if (from === "x" && to === "z") {
1003
- currentIndex = convertIndex[from][to](cursorNumX);
1004
- oldIndex = currentIndex * ratios[to];
1005
- convertCursorNumX = convertCursor[from][to](currentSliceIndex);
1006
- convertCursorNumY = cursorNumY;
1007
- }
1008
- else if (from === "y" && to === "z") {
1009
- currentIndex = convertIndex[from][to](cursorNumY);
1010
- oldIndex = currentIndex * ratios.z;
1011
- convertCursorNumY = convertCursor[from][to](currentSliceIndex);
1012
- convertCursorNumX = cursorNumX;
1013
- }
1014
- else {
1015
- return;
1016
- }
1017
- return { currentIndex, oldIndex, convertCursorNumX, convertCursorNumY };
782
+ return this.crosshairTool.convertCursorPoint(from, to, cursorNumX, cursorNumY, currentSliceIndex);
1018
783
  }
1019
784
  setUpSphereOrigins(mouseX, mouseY, sliceIndex) {
1020
- const convertCursor = (from, to) => {
1021
- const convertObj = this.convertCursorPoint(from, to, mouseX, mouseY, sliceIndex);
1022
- return {
1023
- convertCursorNumX: convertObj === null || convertObj === void 0 ? void 0 : convertObj.convertCursorNumX,
1024
- convertCursorNumY: convertObj === null || convertObj === void 0 ? void 0 : convertObj.convertCursorNumY,
1025
- currentIndex: convertObj === null || convertObj === void 0 ? void 0 : convertObj.currentIndex,
1026
- };
1027
- };
1028
- const axisConversions = {
1029
- x: { axisTo1: "y", axisTo2: "z" },
1030
- y: { axisTo1: "z", axisTo2: "x" },
1031
- z: { axisTo1: "x", axisTo2: "y" },
1032
- };
1033
- const { axisTo1, axisTo2 } = axisConversions[this.protectedData.axis];
1034
- this.nrrd_states.sphereOrigin[axisTo1] = [
1035
- convertCursor(this.protectedData.axis, axisTo1).convertCursorNumX,
1036
- convertCursor(this.protectedData.axis, axisTo1).convertCursorNumY,
1037
- convertCursor(this.protectedData.axis, axisTo1).currentIndex,
1038
- ];
1039
- this.nrrd_states.sphereOrigin[axisTo2] = [
1040
- convertCursor(this.protectedData.axis, axisTo2).convertCursorNumX,
1041
- convertCursor(this.protectedData.axis, axisTo2).convertCursorNumY,
1042
- convertCursor(this.protectedData.axis, axisTo2).currentIndex,
1043
- ];
1044
- }
1045
- /****************************label div controls****************************************************/
1046
- getRestLabel() {
1047
- const labels = this.nrrd_states.labels;
1048
- const restLabel = labels.filter((item) => {
1049
- return item !== this.gui_states.label;
1050
- });
1051
- return restLabel;
785
+ this.crosshairTool.setUpSphereOrigins(mouseX, mouseY, sliceIndex);
1052
786
  }
1053
- /**************************** Undo clear functions****************************************************/
1054
- getCurrentUndo() {
1055
- return this.undoArray.filter((item) => {
1056
- return item.sliceIndex === this.nrrd_states.currentIndex;
787
+ /****************************layer div controls****************************************************/
788
+ getRestLayer() {
789
+ const layers = this.nrrd_states.layers;
790
+ const restLayer = layers.filter((item) => {
791
+ return item !== this.gui_states.layer;
1057
792
  });
793
+ return restLayer;
1058
794
  }
795
+ /**************************** Undo/Redo functions (Phase 6 — Delta-based) ****************************/
1059
796
  /**
1060
- * Clear mask on current slice canvas
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.
1061
802
  */
1062
803
  clearPaint() {
1063
804
  this.protectedData.Is_Draw = true;
1064
- this.resetLayerCanvas();
1065
805
  this.protectedData.canvases.originCanvas.width =
1066
806
  this.protectedData.canvases.originCanvas.width;
1067
807
  this.protectedData.mainPreSlices.repaint.call(this.protectedData.mainPreSlices);
1068
808
  this.protectedData.previousDrawingImage =
1069
809
  this.protectedData.ctxes.emptyCtx.createImageData(1, 1);
1070
- this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.label);
1071
- const restLabels = this.getRestLabel();
1072
- this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[0]);
1073
- this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[1]);
1074
- this.setIsDrawFalse(1000);
1075
- }
1076
- // need to update
1077
- undoLastPainting() {
1078
- let { ctx, canvas } = this.setCurrentLayer();
1079
- this.protectedData.Is_Draw = true;
1080
- this.protectedData.canvases.drawingCanvasLayerMaster.width =
1081
- this.protectedData.canvases.drawingCanvasLayerMaster.width;
1082
- canvas.width = canvas.width;
1083
- this.protectedData.mainPreSlices.repaint.call(this.protectedData.mainPreSlices);
1084
- const currentUndoObj = this.getCurrentUndo();
1085
- if (currentUndoObj.length > 0) {
1086
- const undo = currentUndoObj[0];
1087
- const layerUndos = undo.layers[this.gui_states.label];
1088
- const layerLen = layerUndos.length;
1089
- // if (layerLen === 0) return;
1090
- layerUndos.pop();
1091
- if (layerLen > 0) {
1092
- // const imageSrc = undo.undos[undo.undos.length - 1];
1093
- const image = layerUndos[layerLen - 1];
1094
- if (!!image) {
1095
- ctx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1096
- }
1097
- }
1098
- if (undo.layers.label1.length > 0) {
1099
- const image = undo.layers.label1[undo.layers.label1.length - 1];
1100
- this.protectedData.ctxes.drawingLayerMasterCtx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1101
- }
1102
- if (undo.layers.label2.length > 0) {
1103
- const image = undo.layers.label2[undo.layers.label2.length - 1];
1104
- this.protectedData.ctxes.drawingLayerMasterCtx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1105
- }
1106
- if (undo.layers.label3.length > 0) {
1107
- const image = undo.layers.label3[undo.layers.label3.length - 1];
1108
- this.protectedData.ctxes.drawingLayerMasterCtx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
810
+ // Clear only the active layer's MaskVolume slice and record undo delta
811
+ 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
817
+ const oldSlice = vol.getSliceUint8(idx, axis).data.slice();
818
+ // Clear only the active layer's MaskVolume slice
819
+ vol.clearSlice(idx, axis);
820
+ // New (all-zero) slice for undo newSlice
821
+ const { data: newSlice, width, height } = vol.getSliceUint8(idx, axis);
822
+ // Push clearPaint delta to UndoManager (supports undo)
823
+ this.undoManager.push({
824
+ layerId: activeLayer,
825
+ axis,
826
+ sliceIndex: idx,
827
+ oldSlice,
828
+ newSlice: newSlice.slice(),
829
+ });
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
+ );
1109
835
  }
1110
- this.protectedData.previousDrawingImage =
1111
- this.protectedData.ctxes.drawingLayerMasterCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvasLayerMaster.width, this.protectedData.canvases.drawingCanvasLayerMaster.height);
1112
- this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.label);
1113
- this.setIsDrawFalse(1000);
1114
836
  }
1115
- }
1116
- /****************************Store images****************************************************/
1117
- storeImageToAxis(index, paintedImages, imageData, axis) {
1118
- var _a, _b, _c;
1119
- let temp = {
1120
- index,
1121
- image: imageData,
1122
- };
1123
- let drawedImage;
1124
- switch (!!axis ? axis : this.protectedData.axis) {
1125
- case "x":
1126
- drawedImage = this.filterDrawedImage("x", index, paintedImages);
1127
- drawedImage
1128
- ? (drawedImage.image = imageData)
1129
- : (_a = paintedImages.x) === null || _a === void 0 ? void 0 : _a.push(temp);
1130
- break;
1131
- case "y":
1132
- drawedImage = this.filterDrawedImage("y", index, paintedImages);
1133
- drawedImage
1134
- ? (drawedImage.image = imageData)
1135
- : (_b = paintedImages.y) === null || _b === void 0 ? void 0 : _b.push(temp);
1136
- break;
1137
- case "z":
1138
- drawedImage = this.filterDrawedImage("z", index, paintedImages);
1139
- drawedImage
1140
- ? (drawedImage.image = imageData)
1141
- : (_c = paintedImages.z) === null || _c === void 0 ? void 0 : _c.push(temp);
1142
- break;
837
+ catch (_a) {
838
+ // Volume not ready (1×1×1 placeholder)
1143
839
  }
840
+ // Re-render ALL layers from MaskVolume to canvas (rebuilds visuals from source of truth)
841
+ this.resetLayerCanvas();
842
+ const buffer = this.getOrCreateSliceBuffer(this.protectedData.axis);
843
+ 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);
848
+ if (!target)
849
+ continue;
850
+ target.ctx.clearRect(0, 0, w, h);
851
+ this.renderSliceToCanvas(layerId, this.protectedData.axis, this.nrrd_states.currentIndex, buffer, target.ctx, w, h);
852
+ }
853
+ }
854
+ this.compositeAllLayers();
855
+ this.setIsDrawFalse(1000);
1144
856
  }
1145
- storeAllImages(index, label) {
1146
- // const image: HTMLImageElement = new Image();
1147
- // resize the drawing image data
1148
- if (!this.nrrd_states.loadMaskJson && !this.gui_states.sphere && !this.gui_states.calculator) {
1149
- this.setEmptyCanvasSize();
1150
- this.drawImageOnEmptyImage(this.protectedData.canvases.drawingCanvasLayerMaster);
857
+ /**
858
+ * 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
+ */
862
+ undoLastPainting() {
863
+ const delta = this.undoManager.undo();
864
+ if (!delta)
865
+ return;
866
+ try {
867
+ const vol = this.getVolumeForLayer(delta.layerId);
868
+ vol.setSliceUint8(delta.sliceIndex, delta.oldSlice, delta.axis);
1151
869
  }
1152
- let imageData = this.protectedData.ctxes.emptyCtx.getImageData(0, 0, this.protectedData.canvases.emptyCanvas.width, this.protectedData.canvases.emptyCanvas.height);
1153
- // 1.12.23
1154
- switch (this.protectedData.axis) {
1155
- case "x":
1156
- const maskData_x = this.checkSharedPlaceSlice(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, imageData);
1157
- const marked_a_x = this.sliceArrayV(maskData_x, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_z_pixel);
1158
- const marked_b_x = this.sliceArrayH(maskData_x, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_z_pixel);
1159
- // const ratio_a_x =
1160
- // this.nrrd_states.nrrd_z / this.nrrd_states.dimensions[2];
1161
- // const ratio_b_x =
1162
- // this.nrrd_states.nrrd_y / this.nrrd_states.dimensions[1];
1163
- const convertXIndex = index;
1164
- // from x the target z will replace the col pixel
1165
- this.replaceVerticalColPixels(this.protectedData.maskData.paintImages.z, this.nrrd_states.dimensions[2],
1166
- // this.nrrd_states.ratios.z,
1167
- 1, marked_a_x, this.nrrd_states.nrrd_x_pixel, convertXIndex);
1168
- // from x the target y will replace the col pixel
1169
- this.replaceVerticalColPixels(this.protectedData.maskData.paintImages.y, this.nrrd_states.dimensions[1],
1170
- // this.nrrd_states.ratios.y,
1171
- 1, marked_b_x, this.nrrd_states.nrrd_x_pixel, convertXIndex);
1172
- break;
1173
- case "y":
1174
- const maskData_y = this.checkSharedPlaceSlice(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, imageData);
1175
- const marked_a_y = this.sliceArrayV(maskData_y, this.nrrd_states.nrrd_z_pixel, this.nrrd_states.nrrd_x_pixel);
1176
- const marked_b_y = this.sliceArrayH(maskData_y, this.nrrd_states.nrrd_z_pixel, this.nrrd_states.nrrd_x_pixel);
1177
- // const ratio_a_y =
1178
- // this.nrrd_states.nrrd_x / this.nrrd_states.dimensions[0];
1179
- // const ratio_b_y =
1180
- // this.nrrd_states.nrrd_z / this.nrrd_states.dimensions[2];
1181
- const convertYIndex = index;
1182
- this.replaceHorizontalRowPixels(this.protectedData.maskData.paintImages.x, this.nrrd_states.dimensions[0],
1183
- // this.nrrd_states.ratios.x,
1184
- 1, marked_a_y, this.nrrd_states.nrrd_z_pixel, convertYIndex);
1185
- this.replaceHorizontalRowPixels(this.protectedData.maskData.paintImages.z, this.nrrd_states.dimensions[2],
1186
- // this.nrrd_states.ratios.z,
1187
- 1, marked_b_y, this.nrrd_states.nrrd_x_pixel, convertYIndex);
1188
- break;
1189
- case "z":
1190
- // for x slices get cols' pixels
1191
- // for y slices get rows' pixels
1192
- // 1. slice z 的 y轴对应了slice y的index,所以我们可以通过slice z 确定在y轴上那些行是有pixels的,我们就可以将它的y坐标(或者是行号)对应到slice y的index,并将该index下的marked image提取出来。
1193
- // 2. 接着我们可以通过当前slice z 的index,来确定marked image 需要替换或重组的 行 pixel array。
1194
- const maskData_z = this.checkSharedPlaceSlice(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, imageData);
1195
- // 1. get slice z's each row's and col's pixel as a 2d array.
1196
- // 1.1 get the cols' 2d array for slice x
1197
- const marked_a_z = this.sliceArrayV(maskData_z, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_x_pixel);
1198
- // 1.2 get the rows' 2d array for slice y
1199
- const marked_b_z = this.sliceArrayH(maskData_z, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_x_pixel);
1200
- // 1.3 get x axis ratio for converting, to match the number slice x with the slice z's x axis pixel number.
1201
- // const ratio_a_z =
1202
- // this.nrrd_states.nrrd_x / this.nrrd_states.dimensions[0];
1203
- // // 1.4 get y axis ratio for converting
1204
- // const ratio_b_z =
1205
- // this.nrrd_states.nrrd_y / this.nrrd_states.dimensions[1];
1206
- // 1.5 To identify which row/col data should be replace
1207
- const convertZIndex = index;
1208
- // 2. Mapping coordinates
1209
- // from z the target x will replace the col pixel
1210
- this.replaceVerticalColPixels(this.protectedData.maskData.paintImages.x, this.nrrd_states.dimensions[0],
1211
- // this.nrrd_states.ratios.x,
1212
- 1, marked_a_z, this.nrrd_states.nrrd_z_pixel, convertZIndex);
1213
- // from z the target y will replace row pixel
1214
- this.replaceHorizontalRowPixels(this.protectedData.maskData.paintImages.y, this.nrrd_states.dimensions[1],
1215
- // this.nrrd_states.ratios.y,
1216
- 1, marked_b_z, this.nrrd_states.nrrd_x_pixel, convertZIndex);
1217
- break;
870
+ catch (_a) {
871
+ return; // Volume not ready
1218
872
  }
1219
- this.storeImageToAxis(index, this.protectedData.maskData.paintImages, imageData);
1220
- if (!this.nrrd_states.loadMaskJson && !this.gui_states.sphere && !this.gui_states.calculator) {
1221
- this.storeEachLayerImage(index, label);
873
+ this.protectedData.Is_Draw = true;
874
+ if (delta.axis === this.protectedData.axis && delta.sliceIndex === this.nrrd_states.currentIndex) {
875
+ this.applyUndoRedoToCanvas(delta.layerId);
1222
876
  }
1223
- }
1224
- storeImageToLabel(index, canvas, paintedImages) {
1225
877
  if (!this.nrrd_states.loadMaskJson) {
1226
- this.setEmptyCanvasSize();
1227
- this.drawImageOnEmptyImage(canvas);
878
+ const { data: sliceData, width, height } = this.getVolumeForLayer(delta.layerId)
879
+ .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);
1228
881
  }
1229
- const imageData = this.protectedData.ctxes.emptyCtx.getImageData(0, 0, this.protectedData.canvases.emptyCanvas.width, this.protectedData.canvases.emptyCanvas.height);
1230
- this.storeImageToAxis(index, paintedImages, imageData);
1231
- // this.setEmptyCanvasSize()
1232
- return imageData;
882
+ this.setIsDrawFalse(1000);
1233
883
  }
1234
- storeEachLayerImage(index, label) {
1235
- if (!this.nrrd_states.loadMaskJson) {
1236
- this.setEmptyCanvasSize();
1237
- }
1238
- let imageData;
1239
- switch (label) {
1240
- case "label1":
1241
- imageData = this.storeImageToLabel(index, this.protectedData.canvases.drawingCanvasLayerOne, this.protectedData.maskData.paintImagesLabel1);
1242
- break;
1243
- case "label2":
1244
- imageData = this.storeImageToLabel(index, this.protectedData.canvases.drawingCanvasLayerTwo, this.protectedData.maskData.paintImagesLabel2);
1245
- break;
1246
- case "label3":
1247
- imageData = this.storeImageToLabel(index, this.protectedData.canvases.drawingCanvasLayerThree, this.protectedData.maskData.paintImagesLabel3);
1248
- break;
1249
- }
1250
- // callback function to return the painted image
1251
- if (!this.nrrd_states.loadMaskJson && this.protectedData.axis == "z") {
1252
- this.nrrd_states.getMask(imageData, this.nrrd_states.currentIndex, label, this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.clearAllFlag);
884
+ /**
885
+ * 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
+ */
889
+ redoLastPainting() {
890
+ const delta = this.undoManager.redo();
891
+ if (!delta)
892
+ return;
893
+ try {
894
+ const vol = this.getVolumeForLayer(delta.layerId);
895
+ vol.setSliceUint8(delta.sliceIndex, delta.newSlice, delta.axis);
1253
896
  }
1254
- }
1255
- // slice array to 2d array
1256
- sliceArrayH(arr, row, col) {
1257
- const arr2D = [];
1258
- for (let i = 0; i < row; i++) {
1259
- const start = i * col * 4;
1260
- const end = (i + 1) * col * 4;
1261
- const temp = arr.slice(start, end);
1262
- arr2D.push(temp);
897
+ catch (_a) {
898
+ return; // Volume not ready
1263
899
  }
1264
- return arr2D;
1265
- }
1266
- sliceArrayV(arr, row, col) {
1267
- const arr2D = [];
1268
- const base = col * 4;
1269
- for (let i = 0; i < col; i++) {
1270
- const temp = [];
1271
- for (let j = 0; j < row; j++) {
1272
- const index = base * j + i * 4;
1273
- temp.push(arr[index]);
1274
- temp.push(arr[index + 1]);
1275
- temp.push(arr[index + 2]);
1276
- temp.push(arr[index + 3]);
1277
- }
1278
- arr2D.push(temp);
900
+ this.protectedData.Is_Draw = true;
901
+ if (delta.axis === this.protectedData.axis && delta.sliceIndex === this.nrrd_states.currentIndex) {
902
+ this.applyUndoRedoToCanvas(delta.layerId);
1279
903
  }
1280
- return arr2D;
1281
- }
1282
- /**
1283
- *
1284
- * @param paintImageArray : the target view slice's marked images array
1285
- * @param length : the target view slice's dimention (total slice index num)
1286
- * @param ratio : the target slice image's width/height ratio of its dimention length
1287
- * @param markedArr : current painted image's vertical 2d Array
1288
- * @param targetWidth : the target image width
1289
- * @param convertIndex : Mapping current image's index to target slice image's width/height pixel start point
1290
- */
1291
- replaceVerticalColPixels(paintImageArray, length, ratio, markedArr, targetWidth, convertIndex) {
1292
- for (let i = 0, len = length; i < len; i++) {
1293
- const index = Math.floor(i * ratio);
1294
- const convertImageArray = paintImageArray[i].image.data;
1295
- const mark_data = markedArr[index];
1296
- const base_a = targetWidth * 4;
1297
- for (let j = 0, len = mark_data.length; j < len; j += 4) {
1298
- const start = (j / 4) * base_a + convertIndex * 4;
1299
- convertImageArray[start] = mark_data[j];
1300
- convertImageArray[start + 1] = mark_data[j + 1];
1301
- convertImageArray[start + 2] = mark_data[j + 2];
1302
- convertImageArray[start + 3] = mark_data[j + 3];
1303
- }
904
+ if (!this.nrrd_states.loadMaskJson) {
905
+ const { data: sliceData, width, height } = this.getVolumeForLayer(delta.layerId)
906
+ .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);
1304
908
  }
909
+ this.setIsDrawFalse(1000);
1305
910
  }
1306
911
  /**
1307
- *
1308
- * @param paintImageArray : the target view slice's marked images array
1309
- * @param length : the target view slice's dimention (total slice index num)
1310
- * @param ratio : the target slice image's width/height ratio of its dimention length
1311
- * @param markedArr : current painted image's horizontal 2d Array
1312
- * @param targetWidth : the target image width
1313
- * @param convertIndex : Mapping current image's index to target slice image's width/height pixel start point
912
+ * Re-render a layer canvas from MaskVolume and composite to master.
913
+ * Called after writing oldSlice/newSlice back to the volume during undo/redo.
1314
914
  */
1315
- replaceHorizontalRowPixels(paintImageArray, length, ratio, markedArr, targetWidth, convertIndex) {
1316
- for (let i = 0, len = length; i < len; i++) {
1317
- const index = Math.floor(i * ratio);
1318
- const convertImageArray = paintImageArray[i].image.data;
1319
- const mark_data = markedArr[index];
1320
- const start = targetWidth * convertIndex * 4;
1321
- for (let j = 0, len = mark_data.length; j < len; j++) {
1322
- convertImageArray[start + j] = mark_data[j];
1323
- }
915
+ applyUndoRedoToCanvas(layerId) {
916
+ let target = this.protectedData.layerTargets.get(layerId);
917
+ if (!target) {
918
+ const firstId = this.nrrd_states.layers[0];
919
+ target = this.protectedData.layerTargets.get(firstId);
920
+ }
921
+ const { ctx, canvas } = target;
922
+ // Clear and re-render the affected layer canvas from MaskVolume
923
+ canvas.width = canvas.width;
924
+ const buffer = this.getOrCreateSliceBuffer(this.protectedData.axis);
925
+ if (buffer) {
926
+ this.renderSliceToCanvas(layerId, this.protectedData.axis, this.nrrd_states.currentIndex, buffer, ctx, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1324
927
  }
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);
1325
933
  }
1326
- /****************************** Utils for store image and itksnap core **************************************/
1327
- checkSharedPlaceSlice(width, height, imageData) {
1328
- let maskData = this.protectedData.ctxes.emptyCtx.createImageData(width, height).data;
1329
- if (this.nrrd_states.sharedPlace.z.includes(this.nrrd_states.currentIndex)) {
1330
- const sharedPlaceArr = this.findSliceInSharedPlace();
1331
- sharedPlaceArr.push(imageData);
1332
- if (sharedPlaceArr.length > 0) {
1333
- for (let i = 0; i < sharedPlaceArr.length; i++) {
1334
- this.replaceArray(maskData, sharedPlaceArr[i].data);
1335
- }
1336
- }
1337
- }
1338
- else {
1339
- maskData = imageData.data;
1340
- }
1341
- return maskData;
934
+ /****************************Store images (delegated to ImageStoreHelper)****************************************************/
935
+ storeAllImages(index, layer) {
936
+ this.imageStoreHelper.storeAllImages(index, layer);
1342
937
  }
1343
- // replace Array
1344
- replaceArray(mainArr, replaceArr) {
1345
- for (let i = 0, len = replaceArr.length; i < len; i++) {
1346
- if (replaceArr[i] === 0 || mainArr[i] !== 0) {
1347
- continue;
1348
- }
1349
- else {
1350
- mainArr[i] = replaceArr[i];
1351
- }
1352
- }
938
+ storeImageToLayer(index, canvas) {
939
+ return this.imageStoreHelper.storeImageToLayer(index, canvas);
1353
940
  }
1354
- findSliceInSharedPlace() {
1355
- const sharedPlaceImages = [];
1356
- const base = Math.floor(this.nrrd_states.currentIndex *
1357
- this.nrrd_states.ratios[this.protectedData.axis]);
1358
- for (let i = 1; i <= 3; i++) {
1359
- const index = this.nrrd_states.currentIndex - i;
1360
- if (index < this.nrrd_states.minIndex) {
1361
- break;
1362
- }
1363
- else {
1364
- const newIndex = Math.floor(index * this.nrrd_states.ratios[this.protectedData.axis]);
1365
- if (newIndex === base) {
1366
- sharedPlaceImages.push(this.protectedData.maskData.paintImages[this.protectedData.axis][index].image);
1367
- }
1368
- }
1369
- }
1370
- for (let i = 1; i <= 3; i++) {
1371
- const index = this.nrrd_states.currentIndex + i;
1372
- if (index > this.nrrd_states.maxIndex) {
1373
- break;
1374
- }
1375
- else {
1376
- const newIndex = Math.floor(index * this.nrrd_states.ratios[this.protectedData.axis]);
1377
- if (newIndex === base) {
1378
- sharedPlaceImages.push(this.protectedData.maskData.paintImages[this.protectedData.axis][index].image);
1379
- }
1380
- }
1381
- }
1382
- return sharedPlaceImages;
941
+ storeEachLayerImage(index, layer) {
942
+ this.imageStoreHelper.storeEachLayerImage(index, layer);
1383
943
  }
1384
- /******************************** Utils gui related functions ***************************************/
1385
- /**
1386
- * Set up root container events fns for drag function
1387
- * @param callback
1388
- */
944
+ /******************************** Utils gui related functions (delegated to ContrastTool) ***************************************/
1389
945
  setupConrastEvents(callback) {
1390
- this.contrastEventPrameters.w = this.container.offsetWidth;
1391
- this.contrastEventPrameters.h = this.container.offsetHeight;
1392
- this.contrastEventPrameters.handleOnContrastMouseDown = (ev) => {
1393
- if (ev.button === 0) {
1394
- this.contrastEventPrameters.x = ev.offsetX / this.contrastEventPrameters.x;
1395
- this.contrastEventPrameters.y = ev.offsetY / this.contrastEventPrameters.h;
1396
- this.container.addEventListener("pointermove", this.contrastEventPrameters.handleOnContrastMouseMove);
1397
- }
1398
- };
1399
- this.contrastEventPrameters.handleOnContrastMouseUp = (ev) => {
1400
- this.container.removeEventListener("pointermove", this.contrastEventPrameters.handleOnContrastMouseMove);
1401
- };
1402
- this.contrastEventPrameters.handleOnContrastMouseMove = throttle((ev) => {
1403
- if (this.contrastEventPrameters.y - ev.offsetY / this.contrastEventPrameters.h >= 0) {
1404
- this.contrastEventPrameters.move_y = -Math.ceil((this.contrastEventPrameters.y - ev.offsetY / this.contrastEventPrameters.h) * 10);
1405
- }
1406
- else {
1407
- this.contrastEventPrameters.move_y = -Math.floor((this.contrastEventPrameters.y - ev.offsetY / this.contrastEventPrameters.h) * 10);
1408
- }
1409
- if (this.contrastEventPrameters.move_y !== 0 && Math.abs(this.contrastEventPrameters.move_y) === 1) {
1410
- callback(this.contrastEventPrameters.move_y, "vertical");
1411
- }
1412
- if (this.contrastEventPrameters.x - ev.offsetX / this.contrastEventPrameters.w >= 0) {
1413
- this.contrastEventPrameters.move_x = -Math.ceil((this.contrastEventPrameters.x - ev.offsetX / this.contrastEventPrameters.w) * 10);
1414
- }
1415
- else {
1416
- this.contrastEventPrameters.move_x = -Math.floor((this.contrastEventPrameters.x - ev.offsetX / this.contrastEventPrameters.w) * 10);
1417
- }
1418
- if (this.contrastEventPrameters.move_x !== 0 && Math.abs(this.contrastEventPrameters.move_x) === 1) {
1419
- callback(this.contrastEventPrameters.move_x, "horizental");
1420
- }
1421
- this.contrastEventPrameters.x = ev.offsetX / this.contrastEventPrameters.w;
1422
- this.contrastEventPrameters.y = ev.offsetY / this.contrastEventPrameters.h;
1423
- }, 100);
1424
- }
1425
- updateSlicesContrast(value, flag) {
1426
- switch (flag) {
1427
- case "lowerThreshold":
1428
- this.protectedData.displaySlices.forEach((slice, index) => {
1429
- slice.volume.lowerThreshold = value;
1430
- });
1431
- break;
1432
- case "upperThreshold":
1433
- this.protectedData.displaySlices.forEach((slice, index) => {
1434
- slice.volume.upperThreshold = value;
1435
- });
1436
- break;
1437
- case "windowLow":
1438
- this.protectedData.displaySlices.forEach((slice, index) => {
1439
- slice.volume.windowLow = value;
1440
- });
1441
- break;
1442
- case "windowHigh":
1443
- this.protectedData.displaySlices.forEach((slice, index) => {
1444
- slice.volume.windowHigh = value;
1445
- });
1446
- break;
1447
- }
1448
- this.repraintCurrentContrastSlice();
1449
- }
1450
- repraintCurrentContrastSlice() {
1451
- this.setSyncsliceNum();
1452
- this.protectedData.displaySlices.forEach((slice, index) => {
1453
- slice.repaint.call(slice);
1454
- });
946
+ this.contrastTool.setupConrastEvents(callback);
1455
947
  }
1456
948
  }
1457
949
  //# sourceMappingURL=DrawToolCore.js.map