copper3d 1.15.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 (130) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +105 -0
  3. package/dist/Controls/Copper3dTrackballControls.d.ts +45 -0
  4. package/dist/Controls/Copper3dTrackballControls.js +543 -0
  5. package/dist/Controls/Copper3dTrackballControls.js.map +1 -0
  6. package/dist/Controls/copperControls.d.ts +17 -0
  7. package/dist/Controls/copperControls.js +36 -0
  8. package/dist/Controls/copperControls.js.map +1 -0
  9. package/dist/Loader/convet.d.ts +1 -0
  10. package/dist/Loader/convet.js +80 -0
  11. package/dist/Loader/convet.js.map +1 -0
  12. package/dist/Loader/copperDicomLoader.d.ts +7 -0
  13. package/dist/Loader/copperDicomLoader.js +113 -0
  14. package/dist/Loader/copperDicomLoader.js.map +1 -0
  15. package/dist/Loader/copperGltfLoader.d.ts +3 -0
  16. package/dist/Loader/copperGltfLoader.js +16 -0
  17. package/dist/Loader/copperGltfLoader.js.map +1 -0
  18. package/dist/Loader/copperNrrdLoader.d.ts +13 -0
  19. package/dist/Loader/copperNrrdLoader.js +424 -0
  20. package/dist/Loader/copperNrrdLoader.js.map +1 -0
  21. package/dist/Loader/copperOBJLoader.d.ts +3 -0
  22. package/dist/Loader/copperOBJLoader.js +4 -0
  23. package/dist/Loader/copperOBJLoader.js.map +1 -0
  24. package/dist/Loader/copperVtkLoader.d.ts +8 -0
  25. package/dist/Loader/copperVtkLoader.js +44 -0
  26. package/dist/Loader/copperVtkLoader.js.map +1 -0
  27. package/dist/Renderer/baseRenderer.d.ts +30 -0
  28. package/dist/Renderer/baseRenderer.js +223 -0
  29. package/dist/Renderer/baseRenderer.js.map +1 -0
  30. package/dist/Renderer/copperMSceneRenderer.d.ts +21 -0
  31. package/dist/Renderer/copperMSceneRenderer.js +115 -0
  32. package/dist/Renderer/copperMSceneRenderer.js.map +1 -0
  33. package/dist/Renderer/copperRenderer.d.ts +15 -0
  34. package/dist/Renderer/copperRenderer.js +52 -0
  35. package/dist/Renderer/copperRenderer.js.map +1 -0
  36. package/dist/Renderer/copperRendererOnDemond.d.ts +13 -0
  37. package/dist/Renderer/copperRendererOnDemond.js +44 -0
  38. package/dist/Renderer/copperRendererOnDemond.js.map +1 -0
  39. package/dist/Scene/baseScene.d.ts +38 -0
  40. package/dist/Scene/baseScene.js +180 -0
  41. package/dist/Scene/baseScene.js.map +1 -0
  42. package/dist/Scene/commonSceneMethod.d.ts +45 -0
  43. package/dist/Scene/commonSceneMethod.js +258 -0
  44. package/dist/Scene/commonSceneMethod.js.map +1 -0
  45. package/dist/Scene/copperMScene.d.ts +42 -0
  46. package/dist/Scene/copperMScene.js +325 -0
  47. package/dist/Scene/copperMScene.js.map +1 -0
  48. package/dist/Scene/copperScene.d.ts +34 -0
  49. package/dist/Scene/copperScene.js +303 -0
  50. package/dist/Scene/copperScene.js.map +1 -0
  51. package/dist/Scene/copperSceneOnDemond.d.ts +13 -0
  52. package/dist/Scene/copperSceneOnDemond.js +60 -0
  53. package/dist/Scene/copperSceneOnDemond.js.map +1 -0
  54. package/dist/Utils/add3DLabel.d.ts +18 -0
  55. package/dist/Utils/add3DLabel.js +66 -0
  56. package/dist/Utils/add3DLabel.js.map +1 -0
  57. package/dist/Utils/download.d.ts +1 -0
  58. package/dist/Utils/download.js +21 -0
  59. package/dist/Utils/download.js.map +1 -0
  60. package/dist/Utils/getVOILUT.d.ts +13 -0
  61. package/dist/Utils/getVOILUT.js +78 -0
  62. package/dist/Utils/getVOILUT.js.map +1 -0
  63. package/dist/Utils/kiwrious/configKiwrious.d.ts +4 -0
  64. package/dist/Utils/kiwrious/configKiwrious.js +59 -0
  65. package/dist/Utils/kiwrious/configKiwrious.js.map +1 -0
  66. package/dist/Utils/nrrd_tool.d.ts +187 -0
  67. package/dist/Utils/nrrd_tool.js +2173 -0
  68. package/dist/Utils/nrrd_tool.js.map +1 -0
  69. package/dist/Utils/raycaster.d.ts +9 -0
  70. package/dist/Utils/raycaster.js +75 -0
  71. package/dist/Utils/raycaster.js.map +1 -0
  72. package/dist/Utils/texture2d.d.ts +10 -0
  73. package/dist/Utils/texture2d.js +111 -0
  74. package/dist/Utils/texture2d.js.map +1 -0
  75. package/dist/Utils/utils.d.ts +8 -0
  76. package/dist/Utils/utils.js +141 -0
  77. package/dist/Utils/utils.js.map +1 -0
  78. package/dist/Utils/workers/reformatSaveDataWorker.d.ts +13 -0
  79. package/dist/Utils/workers/reformatSaveDataWorker.js +107 -0
  80. package/dist/Utils/workers/reformatSaveDataWorker.js.map +1 -0
  81. package/dist/bundle.esm.js +68951 -0
  82. package/dist/bundle.umd.js +68982 -0
  83. package/dist/css/style.css +168 -0
  84. package/dist/index.d.ts +21 -0
  85. package/dist/index.js +22 -0
  86. package/dist/index.js.map +1 -0
  87. package/dist/lib/dicom_pharser_dictionary.d.ts +1 -0
  88. package/dist/lib/dicom_pharser_dictionary.js +28116 -0
  89. package/dist/lib/dicom_pharser_dictionary.js.map +1 -0
  90. package/dist/lib/environment/index.d.ts +8 -0
  91. package/dist/lib/environment/index.js +28 -0
  92. package/dist/lib/environment/index.js.map +1 -0
  93. package/dist/lib/three-vignette.d.ts +11 -0
  94. package/dist/lib/three-vignette.js +93 -0
  95. package/dist/lib/three-vignette.js.map +1 -0
  96. package/dist/types/Controls/Copper3dTrackballControls.d.ts +45 -0
  97. package/dist/types/Controls/copperControls.d.ts +17 -0
  98. package/dist/types/Loader/convet.d.ts +1 -0
  99. package/dist/types/Loader/copperDicomLoader.d.ts +7 -0
  100. package/dist/types/Loader/copperGltfLoader.d.ts +3 -0
  101. package/dist/types/Loader/copperNrrdLoader.d.ts +13 -0
  102. package/dist/types/Loader/copperOBJLoader.d.ts +3 -0
  103. package/dist/types/Loader/copperVtkLoader.d.ts +8 -0
  104. package/dist/types/Renderer/baseRenderer.d.ts +30 -0
  105. package/dist/types/Renderer/copperMSceneRenderer.d.ts +21 -0
  106. package/dist/types/Renderer/copperRenderer.d.ts +15 -0
  107. package/dist/types/Renderer/copperRendererOnDemond.d.ts +13 -0
  108. package/dist/types/Scene/baseScene.d.ts +38 -0
  109. package/dist/types/Scene/commonSceneMethod.d.ts +45 -0
  110. package/dist/types/Scene/copperMScene.d.ts +42 -0
  111. package/dist/types/Scene/copperScene.d.ts +34 -0
  112. package/dist/types/Scene/copperSceneOnDemond.d.ts +13 -0
  113. package/dist/types/Utils/add3DLabel.d.ts +18 -0
  114. package/dist/types/Utils/download.d.ts +1 -0
  115. package/dist/types/Utils/getVOILUT.d.ts +13 -0
  116. package/dist/types/Utils/kiwrious/configKiwrious.d.ts +4 -0
  117. package/dist/types/Utils/nrrd_tool.d.ts +187 -0
  118. package/dist/types/Utils/raycaster.d.ts +9 -0
  119. package/dist/types/Utils/texture2d.d.ts +10 -0
  120. package/dist/types/Utils/utils.d.ts +8 -0
  121. package/dist/types/Utils/workers/reformatSaveDataWorker.d.ts +13 -0
  122. package/dist/types/index.d.ts +21 -0
  123. package/dist/types/lib/dicom_pharser_dictionary.d.ts +1 -0
  124. package/dist/types/lib/environment/index.d.ts +8 -0
  125. package/dist/types/lib/three-vignette.d.ts +11 -0
  126. package/dist/types/types/types.d.ts +198 -0
  127. package/dist/types/types.d.ts +198 -0
  128. package/dist/types/types.js +2 -0
  129. package/dist/types/types.js.map +1 -0
  130. package/package.json +60 -0
@@ -0,0 +1,2173 @@
1
+ import * as THREE from "three";
2
+ import { throttle } from "./raycaster";
3
+ import { switchEraserSize } from "./utils";
4
+ import { saveFileAsJson } from "./download";
5
+ import { restructData, convertReformatDataToBlob, } from "./workers/reformatSaveDataWorker";
6
+ export class nrrd_tools {
7
+ constructor(container) {
8
+ // used to store one label all marks
9
+ this.paintImagesLabel1 = { x: [], y: [], z: [] };
10
+ this.paintImagesLabel2 = { x: [], y: [], z: [] };
11
+ this.paintImagesLabel3 = { x: [], y: [], z: [] };
12
+ this.storedPaintImages = {
13
+ label1: this.paintImagesLabel1,
14
+ label2: this.paintImagesLabel2,
15
+ label3: this.paintImagesLabel3,
16
+ };
17
+ this.paintImages = { x: [], y: [], z: [] };
18
+ // store all contrast slices, include x, y, z orientation
19
+ this.allSlicesArray = [];
20
+ // to store all display slices, only include one orientation (e.g, x,y,z) for all contrast slices.
21
+ this.displaySlices = [];
22
+ // Designed for reload displaySlices Array, only one orientation!
23
+ this.backUpDisplaySlices = [];
24
+ this.skipSlicesDic = {};
25
+ // The default axis for all contrast slice is set to "z" orientation.
26
+ // If we want to switch different orientation, we can set the axis outside.
27
+ this.axis = "z";
28
+ // A base conatainer to append displayCanvas and drawingCanvas
29
+ this.mainAreaContainer = document.createElement("div");
30
+ this.showDragNumberDiv = document.createElement("div");
31
+ this.drawingCanvas = document.createElement("canvas");
32
+ this.displayCanvas = document.createElement("canvas");
33
+ this.downloadCanvas = document.createElement("canvas");
34
+ // use to convert the store image with original size in storeAllImages function!
35
+ this.emptyCanvas = document.createElement("canvas");
36
+ this.downloadImage = document.createElement("a");
37
+ this.drawingCanvasLayerMaster = document.createElement("canvas");
38
+ this.drawingCanvasLayerOne = document.createElement("canvas");
39
+ this.drawingCanvasLayerTwo = document.createElement("canvas");
40
+ this.drawingCanvasLayerThree = document.createElement("canvas");
41
+ this.Is_Shift_Pressed = false;
42
+ this.Is_Draw = false;
43
+ this.sensitiveArray = [];
44
+ this.handleWheelMove = () => { };
45
+ this.start = () => { };
46
+ this.undoArray = [];
47
+ this.initState = true;
48
+ this.eraserUrls = [];
49
+ this.nrrd_states = {
50
+ originWidth: 0,
51
+ originHeight: 0,
52
+ nrrd_x_mm: 0,
53
+ nrrd_y_mm: 0,
54
+ nrrd_z_mm: 0,
55
+ nrrd_x_pixel: 0,
56
+ nrrd_y_pixel: 0,
57
+ nrrd_z_pixel: 0,
58
+ changedWidth: 0,
59
+ changedHeight: 0,
60
+ oldIndex: 0,
61
+ currentIndex: 0,
62
+ maxIndex: 0,
63
+ minIndex: 0,
64
+ RSARatio: 0,
65
+ voxelSpacing: [],
66
+ spaceOrigin: [],
67
+ dimensions: [],
68
+ loadMaskJson: false,
69
+ ratios: { x: 1, y: 1, z: 1 },
70
+ sharedPlace: { x: [-1], y: [-1], z: [-1] },
71
+ latestNotEmptyImg: new Image(),
72
+ contrastNum: 0,
73
+ Max_sensitive: 100,
74
+ readyToUpdate: true,
75
+ showContrast: false,
76
+ enableCursorChoose: false,
77
+ isCursorSelect: false,
78
+ cursorPageX: 0,
79
+ cursorPageY: 0,
80
+ Mouse_Over_x: 0,
81
+ Mouse_Over_y: 0,
82
+ Mouse_Over: false,
83
+ stepClear: 1,
84
+ sizeFoctor: 1,
85
+ clearAllFlag: false,
86
+ previousPanelL: -99999,
87
+ previousPanelT: -99999,
88
+ switchSliceFlag: false,
89
+ labels: ["label1", "label2", "label3"],
90
+ getMask: (mask, sliceId, label, width, height, clearAllFlag) => { },
91
+ // defaultPaintCursor:
92
+ // "url(https://raw.githubusercontent.com/LinkunGao/copper3d_icons/main/icons/pencil-black.svg), auto",
93
+ defaultPaintCursor: "url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/dot.svg) 12 12,auto",
94
+ drawStartPos: new THREE.Vector2(1, 1),
95
+ };
96
+ this.cursorPage = {
97
+ x: {
98
+ cursorPageX: 0,
99
+ cursorPageY: 0,
100
+ index: 0,
101
+ updated: false,
102
+ },
103
+ y: {
104
+ cursorPageX: 0,
105
+ cursorPageY: 0,
106
+ index: 0,
107
+ updated: false,
108
+ },
109
+ z: {
110
+ cursorPageX: 0,
111
+ cursorPageY: 0,
112
+ index: 0,
113
+ updated: false,
114
+ },
115
+ };
116
+ this.gui_states = {
117
+ mainAreaSize: 1,
118
+ dragSensitivity: 75,
119
+ Eraser: false,
120
+ globalAlpha: 0.7,
121
+ lineWidth: 2,
122
+ color: "#f50a33",
123
+ segmentation: true,
124
+ fillColor: "#00ff00",
125
+ brushColor: "#00ff00",
126
+ brushAndEraserSize: 15,
127
+ cursor: "dot",
128
+ label: "label1",
129
+ // EraserSize: 25,
130
+ clear: () => {
131
+ // const text = "Are you sure remove annotations on Current slice?";
132
+ // if (confirm(text) === true) {
133
+ // this.clearPaint();
134
+ // }
135
+ this.clearPaint();
136
+ },
137
+ clearAll: () => {
138
+ const text = "Are you sure remove annotations on All slice?";
139
+ if (confirm(text) === true) {
140
+ this.nrrd_states.clearAllFlag = true;
141
+ this.clearPaint();
142
+ this.clearStoreImages();
143
+ }
144
+ this.nrrd_states.clearAllFlag = false;
145
+ },
146
+ undo: () => {
147
+ this.undoLastPainting();
148
+ },
149
+ downloadCurrentMask: () => {
150
+ this.enableDownload();
151
+ },
152
+ resetZoom: () => {
153
+ this.nrrd_states.sizeFoctor = 1;
154
+ this.resizePaintArea(1);
155
+ this.resetPaintArea();
156
+ },
157
+ subView: false,
158
+ subViewScale: 1.0,
159
+ resetView: () => {
160
+ var _a;
161
+ (_a = this.sceneIn) === null || _a === void 0 ? void 0 : _a.resetView();
162
+ },
163
+ exportMarks: () => {
164
+ // const a = [1, 2, 3, 4];
165
+ this.exportData();
166
+ },
167
+ };
168
+ this.drawLine = (x1, y1, x2, y2) => {
169
+ this.drawingCtx.beginPath();
170
+ this.drawingCtx.moveTo(x1, y1);
171
+ this.drawingCtx.lineTo(x2, y2);
172
+ this.drawingCtx.strokeStyle = this.gui_states.color;
173
+ this.drawingCtx.stroke();
174
+ };
175
+ this.container = container;
176
+ this.displayCtx = this.displayCanvas.getContext("2d");
177
+ this.drawingCtx = this.drawingCanvas.getContext("2d");
178
+ this.emptyCtx = this.emptyCanvas.getContext("2d");
179
+ this.drawingLayerMasterCtx = this.drawingCanvasLayerMaster.getContext("2d");
180
+ this.drawingLayerOneCtx = this.drawingCanvasLayerOne.getContext("2d");
181
+ this.drawingLayerTwoCtx = this.drawingCanvasLayerTwo.getContext("2d");
182
+ this.drawingLayerThreeCtx = this.drawingCanvasLayerThree.getContext("2d");
183
+ this.previousDrawingImage = this.emptyCtx.createImageData(1, 1);
184
+ this.init();
185
+ }
186
+ /**
187
+ * A initialise function for nrrd_tools
188
+ */
189
+ init() {
190
+ this.showDragNumberDiv = this.createShowSliceNumberDiv();
191
+ this.mainAreaContainer.classList.add("copper3D_drawingCanvasContainer");
192
+ this.container.appendChild(this.mainAreaContainer);
193
+ this.autoFocusDiv(this.container);
194
+ this.downloadImage.href = "";
195
+ this.downloadImage.target = "_blank";
196
+ for (let i = 0; i < this.nrrd_states.Max_sensitive; i++) {
197
+ this.sensitiveArray.push((i + 1) / 20);
198
+ }
199
+ this.container.addEventListener("keydown", (ev) => {
200
+ if (ev.key === "Shift") {
201
+ this.Is_Shift_Pressed = true;
202
+ this.nrrd_states.enableCursorChoose = false;
203
+ }
204
+ if (ev.key === "s") {
205
+ this.Is_Draw = false;
206
+ this.nrrd_states.enableCursorChoose =
207
+ !this.nrrd_states.enableCursorChoose;
208
+ }
209
+ });
210
+ this.container.addEventListener("keyup", (ev) => {
211
+ if (ev.key === "Shift") {
212
+ this.Is_Shift_Pressed = false;
213
+ }
214
+ });
215
+ }
216
+ /**
217
+ *
218
+ * entry function
219
+ *
220
+ * @param allSlices - all nrrd contrast slices
221
+ * {
222
+ * x:slice,
223
+ * y:slice,
224
+ * z:slice
225
+ * }
226
+ */
227
+ setAllSlices(allSlices) {
228
+ this.allSlicesArray = [...allSlices];
229
+ this.nrrd_states.nrrd_x_mm = this.allSlicesArray[0].z.canvas.width;
230
+ this.nrrd_states.nrrd_y_mm = this.allSlicesArray[0].z.canvas.height;
231
+ this.nrrd_states.nrrd_z_mm = this.allSlicesArray[0].x.canvas.width;
232
+ this.nrrd_states.nrrd_x_pixel =
233
+ this.allSlicesArray[0].x.volume.dimensions[0];
234
+ this.nrrd_states.nrrd_y_pixel =
235
+ this.allSlicesArray[0].x.volume.dimensions[1];
236
+ this.nrrd_states.nrrd_z_pixel =
237
+ this.allSlicesArray[0].x.volume.dimensions[2];
238
+ console.log("mm", this.nrrd_states.nrrd_x_mm);
239
+ console.log("pixel", this.nrrd_states.nrrd_x_pixel);
240
+ this.nrrd_states.voxelSpacing = this.allSlicesArray[0].x.volume.spacing;
241
+ this.nrrd_states.ratios.x = this.allSlicesArray[0].x.volume.spacing[0];
242
+ this.nrrd_states.ratios.y = this.allSlicesArray[0].x.volume.spacing[1];
243
+ this.nrrd_states.ratios.z = this.allSlicesArray[0].x.volume.spacing[2];
244
+ this.nrrd_states.dimensions = this.allSlicesArray[0].x.volume.dimensions;
245
+ this.allSlicesArray.forEach((item, index) => {
246
+ item.x.contrastOrder = index;
247
+ item.y.contrastOrder = index;
248
+ item.z.contrastOrder = index;
249
+ });
250
+ this.nrrd_states.spaceOrigin = this.allSlicesArray[0].x.volume.header.space_origin.map((item) => {
251
+ return item * 1;
252
+ });
253
+ this.nrrd_states.sharedPlace.x = this.getSharedPlace(this.nrrd_states.dimensions[0], this.nrrd_states.ratios.x);
254
+ this.nrrd_states.sharedPlace.y = this.getSharedPlace(this.nrrd_states.dimensions[1], this.nrrd_states.ratios.y);
255
+ this.nrrd_states.sharedPlace.z = this.getSharedPlace(this.nrrd_states.dimensions[2], this.nrrd_states.ratios.z);
256
+ // init paintImages array
257
+ this.initPaintImages(this.nrrd_states.dimensions);
258
+ // init displayslices array, the axis default is "z"
259
+ this.setDisplaySlicesBaseOnAxis();
260
+ this.afterLoadSlice();
261
+ }
262
+ loadingMaskByLabel(masks, index, imageData) {
263
+ let imageDataLable = this.emptyCtx.createImageData(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel);
264
+ this.setEmptyCanvasSize();
265
+ for (let j = 0; j < masks[index].data.length; j++) {
266
+ imageDataLable.data[j] = masks[index].data[j];
267
+ imageData.data[j] += masks[index].data[j];
268
+ }
269
+ return imageDataLable;
270
+ }
271
+ setMasksData(masksData, loadingBar) {
272
+ if (!!masksData) {
273
+ this.nrrd_states.loadMaskJson = true;
274
+ if (loadingBar) {
275
+ let { loadingContainer, progress } = loadingBar;
276
+ loadingContainer.style.display = "flex";
277
+ progress.innerText = "Loading masks data......";
278
+ }
279
+ this.setEmptyCanvasSize();
280
+ const len = masksData["label1"].length;
281
+ for (let i = 0; i < len; i++) {
282
+ let imageData = this.emptyCtx.createImageData(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel);
283
+ let imageDataLabel1, imageDataLabel2, imageDataLabel3;
284
+ if (masksData["label1"][i].data.length > 0) {
285
+ this.setEmptyCanvasSize();
286
+ imageDataLabel1 = this.loadingMaskByLabel(masksData["label1"], i, imageData);
287
+ this.emptyCtx.putImageData(imageDataLabel1, 0, 0);
288
+ this.storeEachLayerImage(i, "label1");
289
+ }
290
+ if (masksData["label2"][i].data.length > 0) {
291
+ this.setEmptyCanvasSize();
292
+ imageDataLabel2 = this.loadingMaskByLabel(masksData["label2"], i, imageData);
293
+ this.emptyCtx.putImageData(imageDataLabel2, 0, 0);
294
+ this.storeEachLayerImage(i, "label2");
295
+ }
296
+ if (masksData["label3"][i].data.length > 0) {
297
+ this.setEmptyCanvasSize();
298
+ imageDataLabel3 = this.loadingMaskByLabel(masksData["label3"], i, imageData);
299
+ this.emptyCtx.putImageData(imageDataLabel3, 0, 0);
300
+ this.storeEachLayerImage(i, "label3");
301
+ }
302
+ this.setEmptyCanvasSize();
303
+ this.emptyCtx.putImageData(imageData, 0, 0);
304
+ this.storeAllImages(i, "default");
305
+ }
306
+ this.nrrd_states.loadMaskJson = false;
307
+ this.gui_states.resetZoom();
308
+ if (loadingBar) {
309
+ loadingBar.loadingContainer.style.display = "none";
310
+ }
311
+ }
312
+ }
313
+ setEraserUrls(urls) {
314
+ this.eraserUrls = urls;
315
+ }
316
+ getCurrentImageDimension() {
317
+ return this.nrrd_states.dimensions;
318
+ }
319
+ getVoxelSpacing() {
320
+ return this.nrrd_states.voxelSpacing;
321
+ }
322
+ getSpaceOrigin() {
323
+ return this.nrrd_states.spaceOrigin;
324
+ }
325
+ getSharedPlace(len, ratio) {
326
+ let old = -1;
327
+ let same = [];
328
+ let temp = new Set();
329
+ for (let i = 0; i < len; i++) {
330
+ const index = Math.floor(i * ratio);
331
+ if (index === old) {
332
+ temp.add(i - 1);
333
+ temp.add(i);
334
+ }
335
+ else {
336
+ old = index;
337
+ }
338
+ }
339
+ temp.forEach((value) => {
340
+ same.push(value);
341
+ });
342
+ return same;
343
+ }
344
+ /**
345
+ * init all painted images for store images
346
+ * @param dimensions
347
+ */
348
+ initPaintImages(dimensions) {
349
+ this.createEmptyPaintImage(dimensions, this.paintImages);
350
+ this.createEmptyPaintImage(dimensions, this.paintImagesLabel1);
351
+ this.createEmptyPaintImage(dimensions, this.paintImagesLabel2);
352
+ this.createEmptyPaintImage(dimensions, this.paintImagesLabel3);
353
+ }
354
+ createEmptyPaintImage(dimensions, paintImages) {
355
+ for (let i = 0; i < dimensions[0]; i++) {
356
+ const markImage_x = this.emptyCtx.createImageData(this.nrrd_states.nrrd_z_pixel, this.nrrd_states.nrrd_y_pixel);
357
+ const initMark_x = {
358
+ index: i,
359
+ image: markImage_x,
360
+ };
361
+ paintImages.x.push(initMark_x);
362
+ }
363
+ // for y slices' marks
364
+ for (let i = 0; i < dimensions[1]; i++) {
365
+ const markImage_y = this.emptyCtx.createImageData(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_z_pixel);
366
+ const initMark_y = {
367
+ index: i,
368
+ image: markImage_y,
369
+ };
370
+ paintImages.y.push(initMark_y);
371
+ }
372
+ // for z slices' marks
373
+ for (let i = 0; i < dimensions[2]; i++) {
374
+ const markImage_z = this.emptyCtx.createImageData(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel);
375
+ const initMark_z = {
376
+ index: i,
377
+ image: markImage_z,
378
+ };
379
+ paintImages.z.push(initMark_z);
380
+ }
381
+ }
382
+ /**
383
+ * Switch all contrast slices' orientation
384
+ * @param {string} aixs:"x" | "y" | "z"
385
+ * */
386
+ setSliceOrientation(axis) {
387
+ if (this.nrrd_states.enableCursorChoose) {
388
+ if (this.axis === "z") {
389
+ this.cursorPage.z.index = this.nrrd_states.currentIndex;
390
+ this.cursorPage.z.cursorPageX = this.nrrd_states.cursorPageX;
391
+ this.cursorPage.z.cursorPageY = this.nrrd_states.cursorPageY;
392
+ }
393
+ else if (this.axis === "x") {
394
+ this.cursorPage.x.index = this.nrrd_states.currentIndex;
395
+ this.cursorPage.x.cursorPageX = this.nrrd_states.cursorPageX;
396
+ this.cursorPage.x.cursorPageY = this.nrrd_states.cursorPageY;
397
+ }
398
+ else if (this.axis === "y") {
399
+ this.cursorPage.y.index = this.nrrd_states.currentIndex;
400
+ this.cursorPage.y.cursorPageX = this.nrrd_states.cursorPageX;
401
+ this.cursorPage.y.cursorPageY = this.nrrd_states.cursorPageY;
402
+ }
403
+ if (axis === "z") {
404
+ if (this.nrrd_states.isCursorSelect && !this.cursorPage.z.updated) {
405
+ if (this.axis === "x") {
406
+ this.nrrd_states.currentIndex = Math.ceil((this.cursorPage.x.cursorPageX / this.nrrd_states.nrrd_z_mm) *
407
+ this.nrrd_states.dimensions[2]);
408
+ this.nrrd_states.oldIndex =
409
+ this.nrrd_states.currentIndex * this.nrrd_states.ratios.z;
410
+ this.nrrd_states.cursorPageX = Math.ceil((this.cursorPage.x.index / this.nrrd_states.dimensions[0]) *
411
+ this.nrrd_states.nrrd_x_mm);
412
+ }
413
+ if (this.axis === "y") {
414
+ this.nrrd_states.currentIndex = Math.ceil((this.cursorPage.y.cursorPageY / this.nrrd_states.nrrd_z_mm) *
415
+ this.nrrd_states.dimensions[2]);
416
+ this.nrrd_states.oldIndex =
417
+ this.nrrd_states.currentIndex * this.nrrd_states.ratios.z;
418
+ this.nrrd_states.cursorPageY = Math.ceil((this.cursorPage.y.index / this.nrrd_states.dimensions[1]) *
419
+ this.nrrd_states.nrrd_y_mm);
420
+ }
421
+ this.cursorPage.z.updated = true;
422
+ }
423
+ else {
424
+ this.nrrd_states.currentIndex = this.cursorPage.z.index;
425
+ this.nrrd_states.oldIndex =
426
+ this.cursorPage.z.index * this.nrrd_states.ratios.z;
427
+ this.nrrd_states.cursorPageX = this.cursorPage.z.cursorPageX;
428
+ this.nrrd_states.cursorPageY = this.cursorPage.z.cursorPageY;
429
+ }
430
+ }
431
+ else if (axis === "x") {
432
+ if (this.nrrd_states.isCursorSelect && !this.cursorPage.x.updated) {
433
+ if (this.axis === "z") {
434
+ this.nrrd_states.currentIndex = Math.ceil((this.cursorPage.z.cursorPageX / this.nrrd_states.nrrd_x_mm) *
435
+ this.nrrd_states.dimensions[0]);
436
+ this.nrrd_states.oldIndex =
437
+ this.nrrd_states.currentIndex * this.nrrd_states.ratios.x;
438
+ this.nrrd_states.cursorPageX = Math.floor((this.cursorPage.z.index / this.nrrd_states.dimensions[2]) *
439
+ this.nrrd_states.nrrd_z_mm);
440
+ }
441
+ if (this.axis === "y") {
442
+ this.nrrd_states.currentIndex = Math.ceil((this.cursorPage.y.cursorPageX / this.nrrd_states.nrrd_y_mm) *
443
+ this.nrrd_states.dimensions[1]);
444
+ this.nrrd_states.oldIndex =
445
+ this.nrrd_states.currentIndex * this.nrrd_states.ratios.x;
446
+ this.nrrd_states.cursorPageX = this.cursorPage.y.cursorPageY;
447
+ this.nrrd_states.cursorPageY = Math.ceil((this.cursorPage.y.index / this.nrrd_states.dimensions[1]) *
448
+ this.nrrd_states.nrrd_y_mm);
449
+ }
450
+ this.cursorPage.x.updated = true;
451
+ }
452
+ else {
453
+ this.nrrd_states.currentIndex = this.cursorPage.x.index;
454
+ this.nrrd_states.oldIndex =
455
+ this.cursorPage.x.index * this.nrrd_states.ratios.x;
456
+ this.nrrd_states.cursorPageX = this.cursorPage.x.cursorPageX;
457
+ this.nrrd_states.cursorPageY = this.cursorPage.x.cursorPageY;
458
+ }
459
+ }
460
+ else if (axis === "y") {
461
+ if (this.nrrd_states.isCursorSelect && !this.cursorPage.y.updated) {
462
+ if (this.axis === "z") {
463
+ this.nrrd_states.currentIndex = Math.ceil((this.cursorPage.z.cursorPageY / this.nrrd_states.nrrd_y_mm) *
464
+ this.nrrd_states.dimensions[1]);
465
+ this.nrrd_states.oldIndex =
466
+ this.nrrd_states.currentIndex * this.nrrd_states.ratios.y;
467
+ this.nrrd_states.cursorPageY = Math.ceil((this.cursorPage.z.index / this.nrrd_states.dimensions[2]) *
468
+ this.nrrd_states.nrrd_z_mm);
469
+ }
470
+ if (this.axis === "x") {
471
+ this.nrrd_states.currentIndex = Math.ceil((this.cursorPage.x.cursorPageY / this.nrrd_states.nrrd_x_mm) *
472
+ this.nrrd_states.dimensions[0]);
473
+ this.nrrd_states.oldIndex =
474
+ this.nrrd_states.currentIndex * this.nrrd_states.ratios.y;
475
+ this.nrrd_states.cursorPageX = Math.ceil((this.cursorPage.x.index / this.nrrd_states.dimensions[0]) *
476
+ this.nrrd_states.nrrd_x_mm);
477
+ this.nrrd_states.cursorPageY = this.cursorPage.x.cursorPageX;
478
+ }
479
+ this.cursorPage.y.updated = true;
480
+ }
481
+ else {
482
+ this.nrrd_states.currentIndex = this.cursorPage.y.index;
483
+ this.nrrd_states.oldIndex =
484
+ this.cursorPage.y.index * this.nrrd_states.ratios.y;
485
+ this.nrrd_states.cursorPageX = this.cursorPage.y.cursorPageX;
486
+ this.nrrd_states.cursorPageY = this.cursorPage.y.cursorPageY;
487
+ }
488
+ }
489
+ if (this.cursorPage.x.updated &&
490
+ this.cursorPage.y.updated &&
491
+ this.cursorPage.z.updated) {
492
+ this.nrrd_states.isCursorSelect = false;
493
+ }
494
+ }
495
+ this.axis = axis;
496
+ this.resetDisplaySlicesStatus();
497
+ }
498
+ addSkip(index) {
499
+ this.skipSlicesDic[index] = this.backUpDisplaySlices[index];
500
+ if (index >= this.displaySlices.length) {
501
+ this.nrrd_states.contrastNum = this.displaySlices.length;
502
+ }
503
+ else {
504
+ this.nrrd_states.contrastNum = index;
505
+ }
506
+ this.resetDisplaySlicesStatus();
507
+ }
508
+ removeSkip(index) {
509
+ this.skipSlicesDic[index] = undefined;
510
+ this.nrrd_states.contrastNum = 0;
511
+ this.resetDisplaySlicesStatus();
512
+ }
513
+ clear() {
514
+ // To effectively reduce the js memory garbage
515
+ this.allSlicesArray.length = 0;
516
+ this.displaySlices.length = 0;
517
+ this.undoArray.length = 0;
518
+ this.paintImages.x.length = 0;
519
+ this.paintImages.y.length = 0;
520
+ this.paintImages.z.length = 0;
521
+ this.paintImagesLabel1.x.length = 0;
522
+ this.paintImagesLabel1.y.length = 0;
523
+ this.paintImagesLabel1.z.length = 0;
524
+ this.paintImagesLabel2.x.length = 0;
525
+ this.paintImagesLabel2.y.length = 0;
526
+ this.paintImagesLabel2.z.length = 0;
527
+ this.paintImagesLabel3.x.length = 0;
528
+ this.paintImagesLabel3.y.length = 0;
529
+ this.paintImagesLabel3.z.length = 0;
530
+ this.clearDictionary(this.skipSlicesDic);
531
+ // this.nrrd_states.previousPanelL = this.nrrd_states.previousPanelT = -99999;
532
+ this.displayCanvas.style.left = this.drawingCanvas.style.left = "";
533
+ this.displayCanvas.style.top = this.drawingCanvas.style.top = "";
534
+ this.backUpDisplaySlices.length = 0;
535
+ this.mainPreSlice = undefined;
536
+ this.currentShowingSlice = undefined;
537
+ this.previousDrawingImage = this.emptyCtx.createImageData(1, 1);
538
+ this.initState = true;
539
+ this.axis = "z";
540
+ this.nrrd_states.sizeFoctor = 1;
541
+ this.resetLayerCanvas();
542
+ this.drawingCanvas.width = this.drawingCanvas.width;
543
+ this.displayCanvas.width = this.displayCanvas.width;
544
+ }
545
+ setSliceMoving(step) {
546
+ if (this.mainPreSlice) {
547
+ this.Is_Draw = true;
548
+ this.setSyncsliceNum();
549
+ this.updateIndex(step);
550
+ this.setIsDrawFalse(1000);
551
+ }
552
+ }
553
+ setShowInMainArea(isShowContrast) {
554
+ this.nrrd_states.showContrast = isShowContrast;
555
+ this.nrrd_states.contrastNum = 0;
556
+ if (this.mainPreSlice) {
557
+ this.redrawMianPreOnDisplayCanvas();
558
+ this.updateShowNumDiv(this.nrrd_states.contrastNum);
559
+ }
560
+ }
561
+ setMainAreaSize(factor) {
562
+ this.nrrd_states.sizeFoctor += factor;
563
+ if (this.nrrd_states.sizeFoctor >= 8) {
564
+ this.nrrd_states.sizeFoctor = 8;
565
+ }
566
+ else if (this.nrrd_states.sizeFoctor <= 1) {
567
+ this.nrrd_states.sizeFoctor = 1;
568
+ }
569
+ this.resizePaintArea(this.nrrd_states.sizeFoctor);
570
+ this.resetPaintArea();
571
+ this.setIsDrawFalse(1000);
572
+ }
573
+ getMaxSliceNum() {
574
+ if (this.nrrd_states.showContrast) {
575
+ return [
576
+ this.nrrd_states.maxIndex,
577
+ this.nrrd_states.maxIndex * this.displaySlices.length,
578
+ ];
579
+ }
580
+ else {
581
+ return [this.nrrd_states.maxIndex];
582
+ }
583
+ }
584
+ getCurrentSlicesNumAndContrastNum() {
585
+ return {
586
+ currentIndex: this.nrrd_states.currentIndex,
587
+ contrastIndex: this.nrrd_states.contrastNum,
588
+ };
589
+ }
590
+ getCurrentSliceIndex() {
591
+ return Math.ceil(this.mainPreSlice.index / this.nrrd_states.RSARatio);
592
+ }
593
+ getIsShowContrastState() {
594
+ return this.nrrd_states.showContrast;
595
+ }
596
+ setIsDrawFalse(target) {
597
+ this.preTimer = setTimeout(() => {
598
+ this.Is_Draw = false;
599
+ if (this.preTimer) {
600
+ window.clearTimeout(this.preTimer);
601
+ this.preTimer = undefined;
602
+ }
603
+ }, target);
604
+ }
605
+ setDisplaySlicesBaseOnAxis() {
606
+ this.displaySlices.length = 0;
607
+ this.backUpDisplaySlices.length = 0;
608
+ this.allSlicesArray.forEach((slices) => {
609
+ this.backUpDisplaySlices.push(slices[this.axis]);
610
+ });
611
+ this.loadDisplaySlicesArray();
612
+ }
613
+ loadDisplaySlicesArray() {
614
+ const remainSlices = Object.values(this.skipSlicesDic);
615
+ if (remainSlices.length === 0) {
616
+ // load all display slices
617
+ this.backUpDisplaySlices.forEach((slice, index) => {
618
+ this.skipSlicesDic[index] = slice;
619
+ this.displaySlices.push(slice);
620
+ });
621
+ }
622
+ else {
623
+ remainSlices.forEach((slice, index) => {
624
+ if (!!slice) {
625
+ this.displaySlices.push(this.backUpDisplaySlices[index]);
626
+ this.skipSlicesDic[index] = this.backUpDisplaySlices[index];
627
+ }
628
+ });
629
+ }
630
+ }
631
+ resetDisplaySlicesStatus() {
632
+ // reload slice data
633
+ this.setDisplaySlicesBaseOnAxis();
634
+ // reset canvas attribute for drag and draw
635
+ this.setupConfigs();
636
+ }
637
+ setupConfigs() {
638
+ // reset main slice
639
+ this.setMainPreSlice();
640
+ // update the max index for drag and slider
641
+ this.updateMaxIndex();
642
+ // reset origin canvas and the nrrd_states origin Width/height
643
+ // reset the current index
644
+ this.setOriginCanvasAndPre();
645
+ // update the show number div on top area
646
+ this.updateShowNumDiv(this.nrrd_states.contrastNum);
647
+ // repaint all contrast images
648
+ this.repraintCurrentContrastSlice();
649
+ // resize the draw/drawOutLayer/display canvas size
650
+ this.resizePaintArea(this.nrrd_states.sizeFoctor);
651
+ this.resetPaintArea();
652
+ }
653
+ setMainPreSlice() {
654
+ this.mainPreSlice = this.displaySlices[0];
655
+ if (this.mainPreSlice) {
656
+ this.nrrd_states.RSARatio = this.mainPreSlice.RSARatio;
657
+ }
658
+ }
659
+ setOriginCanvasAndPre() {
660
+ if (this.mainPreSlice) {
661
+ if (this.nrrd_states.oldIndex > this.nrrd_states.maxIndex)
662
+ this.nrrd_states.oldIndex = this.nrrd_states.maxIndex;
663
+ if (this.initState) {
664
+ this.nrrd_states.oldIndex =
665
+ this.mainPreSlice.initIndex * this.nrrd_states.RSARatio;
666
+ this.nrrd_states.currentIndex = this.mainPreSlice.initIndex;
667
+ }
668
+ else {
669
+ // !need to change
670
+ // todo
671
+ this.mainPreSlice.index = this.nrrd_states.oldIndex;
672
+ }
673
+ this.originCanvas = this.mainPreSlice.canvas;
674
+ this.updateOriginAndChangedWH();
675
+ }
676
+ }
677
+ afterLoadSlice() {
678
+ this.setMainPreSlice();
679
+ this.setOriginCanvasAndPre();
680
+ this.currentShowingSlice = this.mainPreSlice;
681
+ this.nrrd_states.oldIndex =
682
+ this.mainPreSlice.initIndex * this.nrrd_states.RSARatio;
683
+ this.nrrd_states.currentIndex = this.mainPreSlice.initIndex;
684
+ this.undoArray = [
685
+ {
686
+ sliceIndex: this.nrrd_states.currentIndex,
687
+ layers: { label1: [], label2: [], label3: [] },
688
+ },
689
+ ];
690
+ // compute max index
691
+ this.updateMaxIndex();
692
+ this.updateShowNumDiv(this.nrrd_states.contrastNum);
693
+ this.initState = false;
694
+ }
695
+ updateMaxIndex() {
696
+ if (this.mainPreSlice) {
697
+ this.nrrd_states.maxIndex = this.mainPreSlice.MaxIndex;
698
+ }
699
+ }
700
+ updateCurrentContrastSlice() {
701
+ this.currentShowingSlice = this.displaySlices[this.nrrd_states.contrastNum];
702
+ return this.currentShowingSlice;
703
+ }
704
+ createShowSliceNumberDiv() {
705
+ const sliceNumberDiv = document.createElement("div");
706
+ sliceNumberDiv.className = "copper3d_sliceNumber";
707
+ sliceNumberDiv.style.position = "absolute";
708
+ sliceNumberDiv.style.zIndex = "100";
709
+ sliceNumberDiv.style.top = "20px";
710
+ sliceNumberDiv.style.left = "100px";
711
+ return sliceNumberDiv;
712
+ }
713
+ updateOriginAndChangedWH() {
714
+ this.nrrd_states.originWidth = this.originCanvas.width;
715
+ this.nrrd_states.originHeight = this.originCanvas.height;
716
+ this.nrrd_states.changedWidth =
717
+ this.nrrd_states.originWidth * Number(this.gui_states.mainAreaSize);
718
+ this.nrrd_states.changedHeight =
719
+ this.nrrd_states.originWidth * Number(this.gui_states.mainAreaSize);
720
+ this.resizePaintArea(1);
721
+ this.resetPaintArea();
722
+ }
723
+ initAllCanvas() {
724
+ /**
725
+ * display canvas
726
+ */
727
+ this.displayCanvas.style.position = "absolute";
728
+ this.displayCanvas.style.zIndex = "9";
729
+ this.displayCanvas.width = this.nrrd_states.changedWidth;
730
+ this.displayCanvas.height = this.nrrd_states.changedHeight;
731
+ /**
732
+ * drawing canvas
733
+ */
734
+ this.drawingCanvas.style.zIndex = "10";
735
+ this.drawingCanvas.style.position = "absolute";
736
+ this.drawingCanvas.width = this.nrrd_states.changedWidth;
737
+ this.drawingCanvas.height = this.nrrd_states.changedHeight;
738
+ this.drawingCanvas.style.cursor = this.nrrd_states.defaultPaintCursor;
739
+ this.drawingCanvas.oncontextmenu = () => false;
740
+ /**
741
+ * layer1
742
+ * it should be hide, so we don't need to add it to mainAreaContainer
743
+ */
744
+ this.drawingCanvasLayerMaster.width =
745
+ this.drawingCanvasLayerOne.width =
746
+ this.drawingCanvasLayerTwo.width =
747
+ this.drawingCanvasLayerThree.width =
748
+ this.nrrd_states.changedWidth;
749
+ this.drawingCanvasLayerMaster.height =
750
+ this.drawingCanvasLayerOne.height =
751
+ this.drawingCanvasLayerTwo.height =
752
+ this.drawingCanvasLayerThree.height =
753
+ this.nrrd_states.changedHeight;
754
+ /**
755
+ * display and drawing canvas container
756
+ */
757
+ // this.mainAreaContainer.style.width = this.nrrd_states.changedWidth + "px";
758
+ // this.mainAreaContainer.style.height = this.nrrd_states.changedHeight + "px";
759
+ this.mainAreaContainer.style.width =
760
+ this.nrrd_states.originWidth * 8 + "px";
761
+ this.mainAreaContainer.style.height =
762
+ this.nrrd_states.originHeight * 8 + "px";
763
+ this.mainAreaContainer.appendChild(this.displayCanvas);
764
+ this.mainAreaContainer.appendChild(this.drawingCanvas);
765
+ }
766
+ autoFocusDiv(container) {
767
+ container.tabIndex = 10;
768
+ container.addEventListener("mouseover", () => {
769
+ container.focus();
770
+ });
771
+ container.style.outline = "none";
772
+ }
773
+ setSyncsliceNum() {
774
+ this.displaySlices.forEach((slice, index) => {
775
+ if (index !== 0) {
776
+ slice.index = this.mainPreSlice.index;
777
+ }
778
+ });
779
+ }
780
+ repraintCurrentContrastSlice() {
781
+ this.setSyncsliceNum();
782
+ this.displaySlices.forEach((slice, index) => {
783
+ slice.repaint.call(slice);
784
+ });
785
+ }
786
+ repraintAllContrastSlices() {
787
+ this.displaySlices.forEach((slice, index) => {
788
+ slice.volume.repaintAllSlices();
789
+ });
790
+ }
791
+ updateShowNumDiv(contrastNum) {
792
+ if (this.mainPreSlice) {
793
+ if (this.nrrd_states.currentIndex > this.nrrd_states.maxIndex) {
794
+ this.nrrd_states.currentIndex = this.nrrd_states.maxIndex;
795
+ }
796
+ if (this.nrrd_states.showContrast) {
797
+ this.showDragNumberDiv.innerHTML = `ContrastNum: ${contrastNum}/${this.displaySlices.length - 1} SliceNum: ${this.nrrd_states.currentIndex}/${this.nrrd_states.maxIndex}`;
798
+ }
799
+ else {
800
+ this.showDragNumberDiv.innerHTML = `SliceNum: ${this.nrrd_states.currentIndex}/${this.nrrd_states.maxIndex}`;
801
+ }
802
+ }
803
+ }
804
+ appendLoadingbar(loadingbar) {
805
+ this.mainAreaContainer.appendChild(loadingbar);
806
+ }
807
+ drag(opts) {
808
+ let move;
809
+ let y;
810
+ let h = this.container.offsetHeight;
811
+ let sensivity = 1;
812
+ let handleOnMouseUp;
813
+ let handleOnMouseDown;
814
+ let handleOnMouseMove;
815
+ this.sensitiveArray.reverse();
816
+ if (opts === null || opts === void 0 ? void 0 : opts.showNumber) {
817
+ this.container.appendChild(this.showDragNumberDiv);
818
+ }
819
+ handleOnMouseDown = (ev) => {
820
+ // before start drag event, remove wheel event.
821
+ this.drawingCanvas.removeEventListener("wheel", this.handleWheelMove);
822
+ if (ev.button === 0) {
823
+ // this.setSyncsliceNum();
824
+ y = ev.offsetY / h;
825
+ this.container.addEventListener("pointermove", handleOnMouseMove, false);
826
+ sensivity = this.sensitiveArray[this.gui_states.dragSensitivity - 1];
827
+ }
828
+ };
829
+ handleOnMouseMove = throttle((ev) => {
830
+ if (y - ev.offsetY / h >= 0) {
831
+ move = -Math.ceil(((y - ev.offsetY / h) * 10) / sensivity);
832
+ }
833
+ else {
834
+ move = -Math.floor(((y - ev.offsetY / h) * 10) / sensivity);
835
+ }
836
+ this.updateIndex(move);
837
+ (opts === null || opts === void 0 ? void 0 : opts.getSliceNum) &&
838
+ opts.getSliceNum(this.nrrd_states.currentIndex, this.nrrd_states.contrastNum);
839
+ y = ev.offsetY / h;
840
+ }, sensivity * 200);
841
+ handleOnMouseUp = (ev) => {
842
+ // after drag, add the wheel event
843
+ this.drawingCanvas.addEventListener("wheel", this.handleWheelMove);
844
+ this.setSyncsliceNum();
845
+ this.container.removeEventListener("pointermove", handleOnMouseMove, false);
846
+ };
847
+ const configDragMode = () => {
848
+ this.container.style.cursor = "pointer";
849
+ this.container.addEventListener("pointerdown", handleOnMouseDown, true);
850
+ this.container.addEventListener("pointerup", handleOnMouseUp, true);
851
+ };
852
+ configDragMode();
853
+ this.container.addEventListener("keydown", (ev) => {
854
+ if (ev.key === "Shift") {
855
+ this.container.style.cursor = "";
856
+ this.container.removeEventListener("pointerdown", handleOnMouseDown, true);
857
+ this.container.removeEventListener("pointerup", handleOnMouseUp, false);
858
+ this.setIsDrawFalse(1000);
859
+ }
860
+ });
861
+ this.container.addEventListener("keyup", (ev) => {
862
+ if (ev.key === "Shift") {
863
+ configDragMode();
864
+ }
865
+ });
866
+ }
867
+ updateIndex(move) {
868
+ let sliceModifyNum = 0;
869
+ let contrastModifyNum = 0;
870
+ if (this.nrrd_states.showContrast) {
871
+ contrastModifyNum = move % this.displaySlices.length;
872
+ this.nrrd_states.contrastNum += contrastModifyNum;
873
+ if (move > 0) {
874
+ // move forward
875
+ if (this.nrrd_states.currentIndex <= this.nrrd_states.maxIndex) {
876
+ sliceModifyNum = Math.floor(move / this.displaySlices.length);
877
+ if (this.nrrd_states.contrastNum > this.displaySlices.length - 1) {
878
+ sliceModifyNum += 1;
879
+ this.nrrd_states.contrastNum -= this.displaySlices.length;
880
+ }
881
+ }
882
+ else {
883
+ sliceModifyNum = 0;
884
+ }
885
+ }
886
+ else {
887
+ // move back
888
+ sliceModifyNum = Math.ceil(move / this.displaySlices.length);
889
+ if (this.nrrd_states.contrastNum < 0) {
890
+ this.nrrd_states.contrastNum += this.displaySlices.length;
891
+ sliceModifyNum -= 1;
892
+ }
893
+ }
894
+ }
895
+ else {
896
+ sliceModifyNum = move;
897
+ }
898
+ // let newIndex = this.nrrd_states.oldIndex + sliceModifyNum;
899
+ let newIndex = this.nrrd_states.currentIndex + sliceModifyNum;
900
+ if (newIndex != this.nrrd_states.currentIndex ||
901
+ this.nrrd_states.showContrast) {
902
+ if (newIndex > this.nrrd_states.maxIndex) {
903
+ newIndex = this.nrrd_states.maxIndex;
904
+ this.nrrd_states.contrastNum = this.displaySlices.length - 1;
905
+ }
906
+ else if (newIndex < this.nrrd_states.minIndex) {
907
+ newIndex = this.nrrd_states.minIndex;
908
+ this.nrrd_states.contrastNum = 0;
909
+ }
910
+ else {
911
+ this.mainPreSlice.index = newIndex * this.nrrd_states.RSARatio;
912
+ // clear drawing canvas, and display next slicez
913
+ this.setSyncsliceNum();
914
+ if (newIndex != this.nrrd_states.currentIndex) {
915
+ this.nrrd_states.switchSliceFlag = true;
916
+ this.drawingCanvasLayerMaster.width =
917
+ this.drawingCanvasLayerMaster.width;
918
+ this.drawingCanvasLayerOne.width = this.drawingCanvasLayerOne.width;
919
+ this.drawingCanvasLayerTwo.width = this.drawingCanvasLayerTwo.width;
920
+ this.drawingCanvasLayerThree.width =
921
+ this.drawingCanvasLayerThree.width;
922
+ }
923
+ this.displayCanvas.width = this.displayCanvas.width;
924
+ if (this.nrrd_states.changedWidth === 0) {
925
+ this.nrrd_states.changedWidth = this.nrrd_states.originWidth;
926
+ this.nrrd_states.changedHeight = this.nrrd_states.originHeight;
927
+ }
928
+ // get the slice that need to be updated on displayCanvas
929
+ let needToUpdateSlice = this.updateCurrentContrastSlice();
930
+ needToUpdateSlice.repaint.call(needToUpdateSlice);
931
+ this.nrrd_states.currentIndex = newIndex;
932
+ this.drawDragSlice(needToUpdateSlice.canvas);
933
+ }
934
+ this.nrrd_states.oldIndex = newIndex * this.nrrd_states.RSARatio;
935
+ this.updateShowNumDiv(this.nrrd_states.contrastNum);
936
+ }
937
+ }
938
+ drawDragSlice(canvas) {
939
+ this.displayCtx.save();
940
+ // flip images
941
+ this.flipDisplayImageByAxis();
942
+ this.displayCtx.drawImage(canvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
943
+ this.displayCtx.restore();
944
+ if (this.paintImages.x.length > 0 ||
945
+ this.paintImages.y.length > 0 ||
946
+ this.paintImages.z.length > 0) {
947
+ if (this.nrrd_states.switchSliceFlag) {
948
+ this.paintedImage = this.filterDrawedImage(this.axis, this.nrrd_states.currentIndex, this.paintImages);
949
+ this.drawMaskToLabelCtx(this.paintImages, this.drawingLayerMasterCtx);
950
+ this.drawMaskToLabelCtx(this.paintImagesLabel1, this.drawingLayerOneCtx);
951
+ this.drawMaskToLabelCtx(this.paintImagesLabel2, this.drawingLayerTwoCtx);
952
+ this.drawMaskToLabelCtx(this.paintImagesLabel3, this.drawingLayerThreeCtx);
953
+ this.nrrd_states.switchSliceFlag = false;
954
+ }
955
+ }
956
+ }
957
+ drawMaskToLabelCtx(paintedImages, ctx) {
958
+ const paintedImage = this.filterDrawedImage(this.axis, this.nrrd_states.currentIndex, paintedImages);
959
+ if (paintedImage === null || paintedImage === void 0 ? void 0 : paintedImage.image) {
960
+ // redraw the stored data to empty point 2
961
+ this.setEmptyCanvasSize();
962
+ this.emptyCtx.putImageData(paintedImage.image, 0, 0);
963
+ ctx.drawImage(this.emptyCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
964
+ }
965
+ }
966
+ draw(sceneIn, gui, opts) {
967
+ let modeFolder;
968
+ let subViewFolder;
969
+ if (!!opts) {
970
+ this.nrrd_states.getMask = opts === null || opts === void 0 ? void 0 : opts.getMaskData;
971
+ }
972
+ this.sceneIn = sceneIn;
973
+ sceneIn.controls.enabled = false;
974
+ if (this.gui_states.subView === false) {
975
+ sceneIn.subDiv && (sceneIn.subDiv.style.display = "none");
976
+ }
977
+ /**
978
+ * GUI
979
+ */
980
+ modeFolder = gui.addFolder("Mode Parameters");
981
+ if (sceneIn.subDiv) {
982
+ subViewFolder = gui.addFolder("Sub View");
983
+ subViewFolder.add(this.gui_states, "resetView");
984
+ subViewFolder.add(this.gui_states, "subView").onChange((value) => {
985
+ if (value) {
986
+ sceneIn.controls.enabled = true;
987
+ sceneIn.subDiv && (sceneIn.subDiv.style.display = "block");
988
+ }
989
+ else {
990
+ sceneIn.subDiv && (sceneIn.subDiv.style.display = "none");
991
+ sceneIn.controls.enabled = false;
992
+ }
993
+ });
994
+ subViewFolder
995
+ .add(this.gui_states, "subViewScale")
996
+ .min(0.25)
997
+ .max(2)
998
+ .step(0.01)
999
+ .onFinishChange((value) => {
1000
+ sceneIn.subDiv && (sceneIn.subDiv.style.width = 200 * value + "px");
1001
+ sceneIn.subDiv && (sceneIn.subDiv.style.height = 200 * value + "px");
1002
+ });
1003
+ }
1004
+ this.paintOnCanvas(modeFolder);
1005
+ }
1006
+ paintOnCanvas(modeFolder) {
1007
+ var _a, _b, _c, _d;
1008
+ /**
1009
+ * drag paint panel
1010
+ */
1011
+ let leftclicked = false;
1012
+ let rightclicked = false;
1013
+ let panelMoveInnerX = 0;
1014
+ let panelMoveInnerY = 0;
1015
+ // todo
1016
+ // let currentSliceIndex = this.mainPreSlice.index;
1017
+ let currentSliceIndex = this.mainPreSlice.index;
1018
+ // draw lines starts position
1019
+ let Is_Painting = false;
1020
+ let lines = [];
1021
+ this.updateOriginAndChangedWH();
1022
+ this.initAllCanvas();
1023
+ this.configGui(modeFolder);
1024
+ (_a = this.displayCtx) === null || _a === void 0 ? void 0 : _a.save();
1025
+ this.flipDisplayImageByAxis();
1026
+ (_b = this.displayCtx) === null || _b === void 0 ? void 0 : _b.drawImage(this.originCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1027
+ (_c = this.displayCtx) === null || _c === void 0 ? void 0 : _c.restore();
1028
+ this.previousDrawingImage = this.drawingCtx.getImageData(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
1029
+ // let a global variable to store the wheel move event
1030
+ this.handleWheelMove = this.configMouseWheel((_d = this.sceneIn) === null || _d === void 0 ? void 0 : _d.controls);
1031
+ // init to add it
1032
+ this.drawingCanvas.addEventListener("wheel", this.handleWheelMove, {
1033
+ passive: false,
1034
+ });
1035
+ const handleDragPaintPanel = (e) => {
1036
+ this.drawingCanvas.style.cursor = "grabbing";
1037
+ this.nrrd_states.previousPanelL = e.clientX - panelMoveInnerX;
1038
+ this.nrrd_states.previousPanelT = e.clientY - panelMoveInnerY;
1039
+ this.displayCanvas.style.left = this.drawingCanvas.style.left =
1040
+ this.nrrd_states.previousPanelL + "px";
1041
+ this.displayCanvas.style.top = this.drawingCanvas.style.top =
1042
+ this.nrrd_states.previousPanelT + "px";
1043
+ };
1044
+ // throttle(, 80);
1045
+ const handleDisplayMouseMove = (e) => {
1046
+ this.nrrd_states.Mouse_Over_x = e.offsetX;
1047
+ this.nrrd_states.Mouse_Over_y = e.offsetY;
1048
+ if (this.nrrd_states.Mouse_Over_x === undefined) {
1049
+ this.nrrd_states.Mouse_Over_x = e.clientX;
1050
+ this.nrrd_states.Mouse_Over_y = e.clientY;
1051
+ }
1052
+ if (e.type === "mouseout") {
1053
+ this.nrrd_states.Mouse_Over = false;
1054
+ this.drawingCanvas.removeEventListener("mousemove", handleDisplayMouseMove);
1055
+ }
1056
+ else if (e.type === "mouseover") {
1057
+ this.nrrd_states.Mouse_Over = true;
1058
+ this.drawingCanvas.addEventListener("mousemove", handleDisplayMouseMove);
1059
+ }
1060
+ e.preventDefault();
1061
+ };
1062
+ // add canvas event listeners
1063
+ this.drawingCanvas.addEventListener("mouseover", handleDisplayMouseMove);
1064
+ this.drawingCanvas.addEventListener("mouseout", handleDisplayMouseMove);
1065
+ // disable browser right click menu
1066
+ this.drawingCanvas.addEventListener("pointerdown", (e) => {
1067
+ if (leftclicked || rightclicked) {
1068
+ this.drawingCanvas.removeEventListener("pointerup", handlePointerUp);
1069
+ this.drawingLayerMasterCtx.closePath();
1070
+ return;
1071
+ }
1072
+ // when switch slice, clear previousDrawingImage
1073
+ // todo
1074
+ if (currentSliceIndex !== this.mainPreSlice.index) {
1075
+ this.previousDrawingImage = this.emptyCtx.createImageData(1, 1);
1076
+ currentSliceIndex = this.mainPreSlice.index;
1077
+ }
1078
+ // remove it when mouse click down
1079
+ this.drawingCanvas.removeEventListener("wheel", this.handleWheelMove);
1080
+ if (e.button === 0) {
1081
+ if (this.Is_Shift_Pressed) {
1082
+ leftclicked = true;
1083
+ lines = [];
1084
+ Is_Painting = true;
1085
+ this.Is_Draw = true;
1086
+ if (this.gui_states.Eraser) {
1087
+ // this.drawingCanvas.style.cursor =
1088
+ // "url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/eraser/circular-cursor_48.png) 48 48, crosshair";
1089
+ this.eraserUrls.length > 0
1090
+ ? (this.drawingCanvas.style.cursor = switchEraserSize(this.gui_states.brushAndEraserSize, this.eraserUrls))
1091
+ : (this.drawingCanvas.style.cursor = switchEraserSize(this.gui_states.brushAndEraserSize));
1092
+ }
1093
+ else {
1094
+ this.drawingCanvas.style.cursor =
1095
+ this.nrrd_states.defaultPaintCursor;
1096
+ }
1097
+ this.nrrd_states.drawStartPos.set(e.offsetX, e.offsetY);
1098
+ // this.drawingLayerMasterCtx.beginPath();
1099
+ this.drawingCanvas.addEventListener("pointerup", handlePointerUp);
1100
+ this.drawingCanvas.addEventListener("pointermove", handleOnPainterMove);
1101
+ }
1102
+ else if (this.nrrd_states.enableCursorChoose) {
1103
+ this.nrrd_states.cursorPageX =
1104
+ e.offsetX / this.nrrd_states.sizeFoctor;
1105
+ this.nrrd_states.cursorPageY =
1106
+ e.offsetY / this.nrrd_states.sizeFoctor;
1107
+ this.nrrd_states.isCursorSelect = true;
1108
+ switch (this.axis) {
1109
+ case "x":
1110
+ this.cursorPage.x.updated = true;
1111
+ this.cursorPage.y.updated = false;
1112
+ this.cursorPage.z.updated = false;
1113
+ break;
1114
+ case "y":
1115
+ this.cursorPage.x.updated = false;
1116
+ this.cursorPage.y.updated = true;
1117
+ this.cursorPage.z.updated = false;
1118
+ break;
1119
+ case "z":
1120
+ this.cursorPage.x.updated = false;
1121
+ this.cursorPage.y.updated = false;
1122
+ this.cursorPage.z.updated = true;
1123
+ break;
1124
+ }
1125
+ }
1126
+ }
1127
+ else if (e.button === 2) {
1128
+ rightclicked = true;
1129
+ // let offsetX = parseInt(this.drawingCanvas.style.left);
1130
+ // let offsetY = parseInt(this.drawingCanvas.style.top);
1131
+ let offsetX = this.drawingCanvas.offsetLeft;
1132
+ let offsetY = this.drawingCanvas.offsetTop;
1133
+ panelMoveInnerX = e.clientX - offsetX;
1134
+ panelMoveInnerY = e.clientY - offsetY;
1135
+ this.drawingCanvas.style.cursor = "grab";
1136
+ this.drawingCanvas.addEventListener("pointerup", handlePointerUp);
1137
+ this.drawingCanvas.addEventListener("pointermove", handleDragPaintPanel);
1138
+ }
1139
+ else {
1140
+ return;
1141
+ }
1142
+ }, true);
1143
+ const clearArc = this.useEraser();
1144
+ const handleOnPainterMove = (e) => {
1145
+ this.Is_Draw = true;
1146
+ if (Is_Painting) {
1147
+ if (this.gui_states.Eraser) {
1148
+ this.nrrd_states.stepClear = 1;
1149
+ // drawingCtx.clearRect(e.offsetX - 5, e.offsetY - 5, 25, 25);
1150
+ clearArc(e.offsetX, e.offsetY, this.gui_states.brushAndEraserSize);
1151
+ }
1152
+ else {
1153
+ lines.push({ x: e.offsetX, y: e.offsetY });
1154
+ this.paintOnCanvasLayer(e.offsetX, e.offsetY);
1155
+ }
1156
+ }
1157
+ };
1158
+ const redrawPreviousImageToLabelCtx = (ctx, label = "default") => {
1159
+ var _a;
1160
+ let paintImages;
1161
+ switch (label) {
1162
+ case "label1":
1163
+ paintImages = this.paintImagesLabel1;
1164
+ break;
1165
+ case "label2":
1166
+ paintImages = this.paintImagesLabel2;
1167
+ break;
1168
+ case "label3":
1169
+ paintImages = this.paintImagesLabel3;
1170
+ break;
1171
+ default:
1172
+ paintImages = this.paintImages;
1173
+ break;
1174
+ }
1175
+ const tempPreImg = (_a = this.filterDrawedImage(this.axis, this.nrrd_states.currentIndex, paintImages)) === null || _a === void 0 ? void 0 : _a.image;
1176
+ this.emptyCanvas.width = this.emptyCanvas.width;
1177
+ if (tempPreImg && label == "default") {
1178
+ this.previousDrawingImage = tempPreImg;
1179
+ }
1180
+ this.emptyCtx.putImageData(tempPreImg, 0, 0);
1181
+ // draw privous image
1182
+ ctx.drawImage(this.emptyCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1183
+ };
1184
+ const handlePointerUp = (e) => {
1185
+ if (e.button === 0) {
1186
+ if (this.Is_Shift_Pressed || Is_Painting) {
1187
+ leftclicked = false;
1188
+ let { ctx, canvas } = this.setCurrentLayer();
1189
+ ctx.closePath();
1190
+ this.drawingCanvas.removeEventListener("pointermove", handleOnPainterMove);
1191
+ if (!this.gui_states.Eraser) {
1192
+ if (this.gui_states.segmentation) {
1193
+ this.drawingCanvasLayerMaster.width =
1194
+ this.drawingCanvasLayerMaster.width;
1195
+ canvas.width = canvas.width;
1196
+ redrawPreviousImageToLabelCtx(this.drawingLayerMasterCtx);
1197
+ redrawPreviousImageToLabelCtx(ctx, this.gui_states.label);
1198
+ // draw new drawings
1199
+ ctx.beginPath();
1200
+ ctx.moveTo(lines[0].x, lines[0].y);
1201
+ for (let i = 1; i < lines.length; i++) {
1202
+ ctx.lineTo(lines[i].x, lines[i].y);
1203
+ }
1204
+ ctx.closePath();
1205
+ ctx.lineWidth = 1;
1206
+ ctx.fillStyle = this.gui_states.fillColor;
1207
+ ctx.fill();
1208
+ // draw layer to master layer
1209
+ this.drawingLayerMasterCtx.drawImage(canvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1210
+ }
1211
+ }
1212
+ this.previousDrawingImage = this.drawingLayerMasterCtx.getImageData(0, 0, this.drawingCanvasLayerMaster.width, this.drawingCanvasLayerMaster.height);
1213
+ this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.label);
1214
+ if (this.gui_states.Eraser) {
1215
+ const restLabels = this.getRestLabel();
1216
+ this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[0]);
1217
+ this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[1]);
1218
+ }
1219
+ Is_Painting = false;
1220
+ /**
1221
+ * store undo array
1222
+ */
1223
+ const currentUndoObj = this.getCurrentUndo();
1224
+ const src = this.drawingCanvasLayerMaster.toDataURL();
1225
+ const image = new Image();
1226
+ image.src = src;
1227
+ if (currentUndoObj.length > 0) {
1228
+ currentUndoObj[0].layers[this.gui_states.label].push(image);
1229
+ }
1230
+ else {
1231
+ const undoObj = {
1232
+ sliceIndex: this.nrrd_states.currentIndex,
1233
+ layers: { label1: [], label2: [], label3: [] },
1234
+ };
1235
+ undoObj.layers[this.gui_states.label].push(image);
1236
+ this.undoArray.push(undoObj);
1237
+ }
1238
+ }
1239
+ }
1240
+ else if (e.button === 2) {
1241
+ rightclicked = false;
1242
+ this.drawingCanvas.style.cursor = "grab";
1243
+ this.drawingCanvas.removeEventListener("pointermove", handleDragPaintPanel);
1244
+ }
1245
+ else {
1246
+ return;
1247
+ }
1248
+ // add wheel after pointer up
1249
+ this.drawingCanvas.addEventListener("wheel", this.handleWheelMove, {
1250
+ passive: false,
1251
+ });
1252
+ if (!this.gui_states.segmentation) {
1253
+ this.setIsDrawFalse(100);
1254
+ }
1255
+ };
1256
+ this.drawingCanvas.addEventListener("pointerleave", (e) => {
1257
+ Is_Painting = false;
1258
+ // (this.sceneIn as copperScene).controls.enabled = true;
1259
+ if (leftclicked) {
1260
+ leftclicked = false;
1261
+ this.drawingLayerMasterCtx.closePath();
1262
+ this.drawingCanvas.removeEventListener("pointermove", handleOnPainterMove);
1263
+ }
1264
+ if (rightclicked) {
1265
+ rightclicked = false;
1266
+ this.drawingCanvas.style.cursor = "grab";
1267
+ this.drawingCanvas.removeEventListener("pointermove", handleDragPaintPanel);
1268
+ }
1269
+ this.setIsDrawFalse(100);
1270
+ if (this.gui_states.segmentation) {
1271
+ this.setIsDrawFalse(1000);
1272
+ }
1273
+ });
1274
+ this.start = () => {
1275
+ if (this.nrrd_states.readyToUpdate) {
1276
+ this.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1277
+ this.drawingCtx.globalAlpha = this.gui_states.globalAlpha;
1278
+ if (this.Is_Draw) {
1279
+ this.drawingLayerMasterCtx.lineCap = "round";
1280
+ this.drawingLayerMasterCtx.globalAlpha = 1;
1281
+ this.drawingLayerOneCtx.lineCap = "round";
1282
+ this.drawingLayerOneCtx.globalAlpha = 1;
1283
+ this.drawingLayerTwoCtx.lineCap = "round";
1284
+ this.drawingLayerTwoCtx.globalAlpha = 1;
1285
+ this.drawingLayerThreeCtx.lineCap = "round";
1286
+ this.drawingLayerThreeCtx.globalAlpha = 1;
1287
+ }
1288
+ else {
1289
+ if (this.Is_Shift_Pressed) {
1290
+ if (!this.gui_states.segmentation &&
1291
+ !this.gui_states.Eraser &&
1292
+ this.nrrd_states.Mouse_Over) {
1293
+ this.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1294
+ this.drawingCtx.fillStyle = this.gui_states.brushColor;
1295
+ this.drawingCtx.beginPath();
1296
+ this.drawingCtx.arc(this.nrrd_states.Mouse_Over_x, this.nrrd_states.Mouse_Over_y, this.gui_states.brushAndEraserSize / 2 + 1, 0, Math.PI * 2);
1297
+ // this.drawingCtx.fill();
1298
+ this.drawingCtx.strokeStyle = this.gui_states.brushColor;
1299
+ this.drawingCtx.stroke();
1300
+ }
1301
+ }
1302
+ if (this.nrrd_states.enableCursorChoose) {
1303
+ this.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1304
+ const ex = this.nrrd_states.cursorPageX * this.nrrd_states.sizeFoctor;
1305
+ const ey = this.nrrd_states.cursorPageY * this.nrrd_states.sizeFoctor;
1306
+ this.drawLine(ex, 0, ex, this.drawingCanvas.height);
1307
+ this.drawLine(0, ey, this.drawingCanvas.width, ey);
1308
+ }
1309
+ }
1310
+ this.drawingCtx.drawImage(this.drawingCanvasLayerMaster, 0, 0);
1311
+ }
1312
+ else {
1313
+ this.redrawDisplayCanvas();
1314
+ }
1315
+ };
1316
+ document.addEventListener("keydown", (e) => {
1317
+ if ((e.ctrlKey || e.metaKey) && e.code === "KeyZ") {
1318
+ this.undoLastPainting();
1319
+ }
1320
+ });
1321
+ }
1322
+ // need to update
1323
+ undoLastPainting() {
1324
+ let { ctx, canvas } = this.setCurrentLayer();
1325
+ this.Is_Draw = true;
1326
+ this.drawingCanvasLayerMaster.width = this.drawingCanvasLayerMaster.width;
1327
+ canvas.width = canvas.width;
1328
+ this.mainPreSlice.repaint.call(this.mainPreSlice);
1329
+ const currentUndoObj = this.getCurrentUndo();
1330
+ if (currentUndoObj.length > 0) {
1331
+ const undo = currentUndoObj[0];
1332
+ const layerUndos = undo.layers[this.gui_states.label];
1333
+ const layerLen = layerUndos.length;
1334
+ // if (layerLen === 0) return;
1335
+ layerUndos.pop();
1336
+ if (layerLen > 0) {
1337
+ // const imageSrc = undo.undos[undo.undos.length - 1];
1338
+ const image = layerUndos[layerLen - 1];
1339
+ ctx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1340
+ }
1341
+ if (undo.layers.label1.length > 0) {
1342
+ const image = undo.layers.label1[undo.layers.label1.length - 1];
1343
+ this.drawingLayerMasterCtx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1344
+ }
1345
+ if (undo.layers.label2.length > 0) {
1346
+ const image = undo.layers.label2[undo.layers.label2.length - 1];
1347
+ this.drawingLayerMasterCtx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1348
+ }
1349
+ if (undo.layers.label3.length > 0) {
1350
+ const image = undo.layers.label3[undo.layers.label3.length - 1];
1351
+ this.drawingLayerMasterCtx.drawImage(image, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1352
+ }
1353
+ this.previousDrawingImage = this.drawingLayerMasterCtx.getImageData(0, 0, this.drawingCanvasLayerMaster.width, this.drawingCanvasLayerMaster.height);
1354
+ this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.label);
1355
+ this.setIsDrawFalse(1000);
1356
+ }
1357
+ }
1358
+ getCurrentUndo() {
1359
+ return this.undoArray.filter((item) => {
1360
+ return item.sliceIndex === this.nrrd_states.currentIndex;
1361
+ });
1362
+ }
1363
+ configMouseWheel(controls) {
1364
+ let moveDistance = 1;
1365
+ const handleWheelMove = (e) => {
1366
+ if (this.Is_Shift_Pressed) {
1367
+ return;
1368
+ }
1369
+ e.preventDefault();
1370
+ // this.nrrd_states.originWidth;
1371
+ const delta = e.detail ? e.detail > 0 : e.wheelDelta < 0;
1372
+ this.Is_Draw = true;
1373
+ const ratioL = (e.clientX -
1374
+ this.mainAreaContainer.offsetLeft -
1375
+ this.drawingCanvas.offsetLeft) /
1376
+ this.drawingCanvas.offsetWidth;
1377
+ const ratioT = (e.clientY -
1378
+ this.mainAreaContainer.offsetTop -
1379
+ this.drawingCanvas.offsetTop) /
1380
+ this.drawingCanvas.offsetHeight;
1381
+ const ratioDelta = !delta ? 1 + 0.1 : 1 - 0.1;
1382
+ const w = this.drawingCanvas.offsetWidth * ratioDelta;
1383
+ const h = this.drawingCanvas.offsetHeight * ratioDelta;
1384
+ const l = Math.round(e.clientX - this.mainAreaContainer.offsetLeft - w * ratioL);
1385
+ const t = Math.round(e.clientY - this.mainAreaContainer.offsetTop - h * ratioT);
1386
+ moveDistance = w / this.nrrd_states.originWidth;
1387
+ if (moveDistance > 8) {
1388
+ moveDistance = 8;
1389
+ }
1390
+ else if (moveDistance < 1) {
1391
+ moveDistance = 1;
1392
+ }
1393
+ else {
1394
+ this.resizePaintArea(moveDistance);
1395
+ this.resetPaintArea(l, t);
1396
+ controls && (controls.enabled = false);
1397
+ this.setIsDrawFalse(1000);
1398
+ }
1399
+ this.nrrd_states.sizeFoctor = moveDistance;
1400
+ };
1401
+ return handleWheelMove;
1402
+ }
1403
+ useEraser() {
1404
+ const clearArc = (x, y, radius) => {
1405
+ var calcWidth = radius - this.nrrd_states.stepClear;
1406
+ var calcHeight = Math.sqrt(radius * radius - calcWidth * calcWidth);
1407
+ var posX = x - calcWidth;
1408
+ var posY = y - calcHeight;
1409
+ var widthX = 2 * calcWidth;
1410
+ var heightY = 2 * calcHeight;
1411
+ if (this.nrrd_states.stepClear <= radius) {
1412
+ this.drawingLayerMasterCtx.clearRect(posX, posY, widthX, heightY);
1413
+ this.drawingLayerOneCtx.clearRect(posX, posY, widthX, heightY);
1414
+ this.drawingLayerTwoCtx.clearRect(posX, posY, widthX, heightY);
1415
+ this.drawingLayerThreeCtx.clearRect(posX, posY, widthX, heightY);
1416
+ this.nrrd_states.stepClear += 1;
1417
+ clearArc(x, y, radius);
1418
+ }
1419
+ };
1420
+ return clearArc;
1421
+ }
1422
+ clearPaint() {
1423
+ this.Is_Draw = true;
1424
+ this.resetLayerCanvas();
1425
+ this.originCanvas.width = this.originCanvas.width;
1426
+ this.mainPreSlice.repaint.call(this.mainPreSlice);
1427
+ this.previousDrawingImage = this.emptyCtx.createImageData(1, 1);
1428
+ this.storeAllImages(this.nrrd_states.currentIndex, this.gui_states.label);
1429
+ const restLabels = this.getRestLabel();
1430
+ this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[0]);
1431
+ this.storeEachLayerImage(this.nrrd_states.currentIndex, restLabels[1]);
1432
+ this.setIsDrawFalse(1000);
1433
+ }
1434
+ getRestLabel() {
1435
+ const labels = this.nrrd_states.labels;
1436
+ const restLabel = labels.filter((item) => {
1437
+ return item !== this.gui_states.label;
1438
+ });
1439
+ return restLabel;
1440
+ }
1441
+ clearStoreImages() {
1442
+ this.paintImages.x.length = 0;
1443
+ this.paintImages.y.length = 0;
1444
+ this.paintImages.z.length = 0;
1445
+ this.paintImagesLabel1.x.length = 0;
1446
+ this.paintImagesLabel1.y.length = 0;
1447
+ this.paintImagesLabel1.z.length = 0;
1448
+ this.paintImagesLabel2.x.length = 0;
1449
+ this.paintImagesLabel2.y.length = 0;
1450
+ this.paintImagesLabel2.z.length = 0;
1451
+ this.paintImagesLabel3.x.length = 0;
1452
+ this.paintImagesLabel3.y.length = 0;
1453
+ this.paintImagesLabel3.z.length = 0;
1454
+ this.initPaintImages(this.nrrd_states.dimensions);
1455
+ }
1456
+ enableDownload() {
1457
+ this.downloadImage.download = `slice_${this.axis}_#${this.nrrd_states.currentIndex}`;
1458
+ const downloadCtx = this.downloadCanvas.getContext("2d");
1459
+ this.downloadCanvas.width = this.nrrd_states.originWidth;
1460
+ this.downloadCanvas.height = this.nrrd_states.originHeight;
1461
+ downloadCtx.drawImage(this.drawingCanvas, 0, 0, this.nrrd_states.originWidth, this.nrrd_states.originHeight);
1462
+ this.downloadImage.href = this.downloadCanvas.toDataURL();
1463
+ this.downloadImage.click();
1464
+ }
1465
+ paintOnCanvasLayer(x, y) {
1466
+ let { ctx, canvas } = this.setCurrentLayer();
1467
+ this.drawLinesOnLayer(ctx, x, y);
1468
+ this.drawLinesOnLayer(this.drawingLayerMasterCtx, x, y);
1469
+ // reset drawing start position to current position.
1470
+ this.nrrd_states.drawStartPos.set(x, y);
1471
+ // need to flag the map as needing updating.
1472
+ this.mainPreSlice.mesh.material.map.needsUpdate = true;
1473
+ }
1474
+ drawLinesOnLayer(ctx, x, y) {
1475
+ ctx.beginPath();
1476
+ ctx.moveTo(this.nrrd_states.drawStartPos.x, this.nrrd_states.drawStartPos.y);
1477
+ if (this.gui_states.segmentation) {
1478
+ ctx.strokeStyle = this.gui_states.color;
1479
+ ctx.lineWidth = this.gui_states.lineWidth;
1480
+ }
1481
+ else {
1482
+ ctx.strokeStyle = this.gui_states.brushColor;
1483
+ ctx.lineWidth = this.gui_states.brushAndEraserSize;
1484
+ }
1485
+ ctx.lineTo(x, y);
1486
+ ctx.stroke();
1487
+ ctx.closePath();
1488
+ }
1489
+ setCurrentLayer() {
1490
+ let ctx;
1491
+ let canvas;
1492
+ switch (this.gui_states.label) {
1493
+ case "label1":
1494
+ ctx = this.drawingLayerOneCtx;
1495
+ canvas = this.drawingCanvasLayerOne;
1496
+ break;
1497
+ case "label2":
1498
+ ctx = this.drawingLayerTwoCtx;
1499
+ canvas = this.drawingCanvasLayerTwo;
1500
+ break;
1501
+ case "label3":
1502
+ ctx = this.drawingLayerThreeCtx;
1503
+ canvas = this.drawingCanvasLayerThree;
1504
+ break;
1505
+ default:
1506
+ ctx = this.drawingLayerOneCtx;
1507
+ canvas = this.drawingCanvasLayerOne;
1508
+ break;
1509
+ }
1510
+ return { ctx, canvas };
1511
+ }
1512
+ configGui(modeFolder) {
1513
+ if (modeFolder.__controllers.length > 0)
1514
+ this.removeGuiFolderChilden(modeFolder);
1515
+ modeFolder.open();
1516
+ const actionsFolder = modeFolder.addFolder("Default Actions");
1517
+ actionsFolder
1518
+ .add(this.gui_states, "label", ["label1", "label2", "label3"])
1519
+ .onChange((val) => {
1520
+ if (val === "label1") {
1521
+ this.gui_states.fillColor = "#00ff00";
1522
+ this.gui_states.brushColor = "#00ff00";
1523
+ }
1524
+ else if (val === "label2") {
1525
+ this.gui_states.fillColor = "#ff0000";
1526
+ this.gui_states.brushColor = "#ff0000";
1527
+ }
1528
+ else if (val === "label3") {
1529
+ this.gui_states.fillColor = "#0000ff";
1530
+ this.gui_states.brushColor = "#0000ff";
1531
+ }
1532
+ });
1533
+ actionsFolder
1534
+ .add(this.gui_states, "cursor", ["crosshair", "pencil", "dot"])
1535
+ .name("cursor icons")
1536
+ .onChange((value) => {
1537
+ if (value === "crosshair") {
1538
+ this.nrrd_states.defaultPaintCursor = "crosshair";
1539
+ }
1540
+ if (value === "pencil") {
1541
+ this.nrrd_states.defaultPaintCursor =
1542
+ "url(https://raw.githubusercontent.com/LinkunGao/copper3d_icons/main/icons/pencil-black.svg), auto";
1543
+ }
1544
+ if (value === "dot") {
1545
+ this.nrrd_states.defaultPaintCursor =
1546
+ "url(https://raw.githubusercontent.com/LinkunGao/copper3d-datasets/main/icons/dot.svg) 12 12,auto";
1547
+ }
1548
+ this.drawingCanvas.style.cursor = this.nrrd_states.defaultPaintCursor;
1549
+ });
1550
+ actionsFolder
1551
+ .add(this.gui_states, "mainAreaSize")
1552
+ .name("zoom")
1553
+ .min(1)
1554
+ .max(8)
1555
+ .onFinishChange((factor) => {
1556
+ this.resetPaintArea();
1557
+ this.nrrd_states.sizeFoctor = factor;
1558
+ this.resizePaintArea(factor);
1559
+ });
1560
+ actionsFolder.add(this.gui_states, "resetZoom");
1561
+ actionsFolder
1562
+ .add(this.gui_states, "globalAlpha")
1563
+ .name("opacity")
1564
+ .min(0.1)
1565
+ .max(1)
1566
+ .step(0.01);
1567
+ actionsFolder.add(this.gui_states, "segmentation").name("Pencil");
1568
+ actionsFolder
1569
+ .add(this.gui_states, "brushAndEraserSize")
1570
+ .min(5)
1571
+ .max(50)
1572
+ .step(1)
1573
+ .onChange(() => {
1574
+ if (this.gui_states.Eraser) {
1575
+ this.eraserUrls.length > 0
1576
+ ? (this.drawingCanvas.style.cursor = switchEraserSize(this.gui_states.brushAndEraserSize, this.eraserUrls))
1577
+ : (this.drawingCanvas.style.cursor = switchEraserSize(this.gui_states.brushAndEraserSize));
1578
+ }
1579
+ });
1580
+ actionsFolder.add(this.gui_states, "Eraser").onChange((value) => {
1581
+ this.gui_states.Eraser = value;
1582
+ if (this.gui_states.Eraser) {
1583
+ this.eraserUrls.length > 0
1584
+ ? (this.drawingCanvas.style.cursor = switchEraserSize(this.gui_states.brushAndEraserSize, this.eraserUrls))
1585
+ : (this.drawingCanvas.style.cursor = switchEraserSize(this.gui_states.brushAndEraserSize));
1586
+ }
1587
+ else {
1588
+ this.drawingCanvas.style.cursor = this.nrrd_states.defaultPaintCursor;
1589
+ }
1590
+ });
1591
+ actionsFolder.add(this.gui_states, "clear");
1592
+ actionsFolder.add(this.gui_states, "clearAll");
1593
+ actionsFolder.add(this.gui_states, "undo");
1594
+ actionsFolder
1595
+ .add(this.mainPreSlice.volume, "windowHigh", this.mainPreSlice.volume.min, this.mainPreSlice.volume.max, 1)
1596
+ .name("Image contrast")
1597
+ .onChange((value) => {
1598
+ this.nrrd_states.readyToUpdate = false;
1599
+ this.updateSlicesContrast(value, "windowHigh");
1600
+ })
1601
+ .onFinishChange(() => {
1602
+ this.repraintAllContrastSlices();
1603
+ this.nrrd_states.readyToUpdate = true;
1604
+ });
1605
+ actionsFolder.add(this.gui_states, "exportMarks");
1606
+ const advanceFolder = modeFolder.addFolder("Advance settings");
1607
+ advanceFolder
1608
+ .add(this.gui_states, "dragSensitivity")
1609
+ .min(1)
1610
+ .max(this.nrrd_states.Max_sensitive)
1611
+ .step(1);
1612
+ const segmentationFolder = advanceFolder.addFolder("Pencil settings");
1613
+ segmentationFolder
1614
+ .add(this.gui_states, "lineWidth")
1615
+ .name("outerLineWidth")
1616
+ .min(1.7)
1617
+ .max(3)
1618
+ .step(0.01);
1619
+ segmentationFolder.addColor(this.gui_states, "color");
1620
+ segmentationFolder.addColor(this.gui_states, "fillColor");
1621
+ const bushFolder = advanceFolder.addFolder("Brush settings");
1622
+ bushFolder.addColor(this.gui_states, "brushColor");
1623
+ // modeFolder.add(this.stateMode, "EraserSize").min(1).max(50).step(1);
1624
+ advanceFolder.add(this.gui_states, "downloadCurrentMask");
1625
+ const contrastFolder = advanceFolder.addFolder("contrast advance settings");
1626
+ contrastFolder
1627
+ .add(this.mainPreSlice.volume, "lowerThreshold", this.mainPreSlice.volume.min, this.mainPreSlice.volume.max, 1)
1628
+ .name("Lower Threshold")
1629
+ .onChange((value) => {
1630
+ this.nrrd_states.readyToUpdate = false;
1631
+ this.updateSlicesContrast(value, "lowerThreshold");
1632
+ })
1633
+ .onFinishChange(() => {
1634
+ this.repraintAllContrastSlices();
1635
+ this.nrrd_states.readyToUpdate = true;
1636
+ });
1637
+ contrastFolder
1638
+ .add(this.mainPreSlice.volume, "upperThreshold", this.mainPreSlice.volume.min, this.mainPreSlice.volume.max, 1)
1639
+ .name("Upper Threshold")
1640
+ .onChange((value) => {
1641
+ this.nrrd_states.readyToUpdate = false;
1642
+ this.updateSlicesContrast(value, "upperThreshold");
1643
+ })
1644
+ .onFinishChange(() => {
1645
+ this.repraintAllContrastSlices();
1646
+ this.nrrd_states.readyToUpdate = true;
1647
+ });
1648
+ contrastFolder
1649
+ .add(this.mainPreSlice.volume, "windowLow", this.mainPreSlice.volume.min, this.mainPreSlice.volume.max, 1)
1650
+ .name("Window Low")
1651
+ .onChange((value) => {
1652
+ this.nrrd_states.readyToUpdate = false;
1653
+ this.updateSlicesContrast(value, "windowLow");
1654
+ })
1655
+ .onFinishChange(() => {
1656
+ this.repraintAllContrastSlices();
1657
+ this.nrrd_states.readyToUpdate = true;
1658
+ });
1659
+ actionsFolder.open();
1660
+ }
1661
+ updateSlicesContrast(value, flag) {
1662
+ switch (flag) {
1663
+ case "lowerThreshold":
1664
+ this.displaySlices.forEach((slice, index) => {
1665
+ slice.volume.lowerThreshold = value;
1666
+ });
1667
+ break;
1668
+ case "upperThreshold":
1669
+ this.displaySlices.forEach((slice, index) => {
1670
+ slice.volume.upperThreshold = value;
1671
+ });
1672
+ break;
1673
+ case "windowLow":
1674
+ this.displaySlices.forEach((slice, index) => {
1675
+ slice.volume.windowLow = value;
1676
+ });
1677
+ break;
1678
+ case "windowHigh":
1679
+ this.displaySlices.forEach((slice, index) => {
1680
+ slice.volume.windowHigh = value;
1681
+ });
1682
+ break;
1683
+ }
1684
+ this.repraintCurrentContrastSlice();
1685
+ }
1686
+ resetPaintArea(l, t) {
1687
+ if (l && t) {
1688
+ this.displayCanvas.style.left = this.drawingCanvas.style.left = l + "px";
1689
+ this.displayCanvas.style.top = this.drawingCanvas.style.top = t + "px";
1690
+ }
1691
+ else {
1692
+ this.mainAreaContainer.style.justifyContent = "center";
1693
+ this.mainAreaContainer.style.alignItems = "center";
1694
+ }
1695
+ }
1696
+ resetLayerCanvas() {
1697
+ this.drawingCanvasLayerMaster.width = this.drawingCanvasLayerMaster.width;
1698
+ this.drawingCanvasLayerOne.width = this.drawingCanvasLayerOne.width;
1699
+ this.drawingCanvasLayerTwo.width = this.drawingCanvasLayerTwo.width;
1700
+ this.drawingCanvasLayerThree.width = this.drawingCanvasLayerThree.width;
1701
+ }
1702
+ redrawDisplayCanvas() {
1703
+ var _a, _b, _c;
1704
+ this.updateCurrentContrastSlice();
1705
+ this.displayCanvas.width = this.displayCanvas.width;
1706
+ this.displayCanvas.height = this.displayCanvas.height;
1707
+ this.originCanvas.width = this.originCanvas.width;
1708
+ if (this.currentShowingSlice) {
1709
+ this.currentShowingSlice.repaint.call(this.currentShowingSlice);
1710
+ (_a = this.displayCtx) === null || _a === void 0 ? void 0 : _a.save();
1711
+ this.flipDisplayImageByAxis();
1712
+ (_b = this.displayCtx) === null || _b === void 0 ? void 0 : _b.drawImage(this.currentShowingSlice.canvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1713
+ (_c = this.displayCtx) === null || _c === void 0 ? void 0 : _c.restore();
1714
+ }
1715
+ }
1716
+ redrawMianPreOnDisplayCanvas() {
1717
+ var _a;
1718
+ this.displayCanvas.width = this.displayCanvas.width;
1719
+ this.displayCanvas.height = this.displayCanvas.height;
1720
+ this.originCanvas.width = this.originCanvas.width;
1721
+ if (this.mainPreSlice) {
1722
+ this.mainPreSlice.repaint.call(this.mainPreSlice);
1723
+ this.flipDisplayImageByAxis();
1724
+ (_a = this.displayCtx) === null || _a === void 0 ? void 0 : _a.drawImage(this.originCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1725
+ this.resizePaintArea(this.nrrd_states.sizeFoctor);
1726
+ }
1727
+ }
1728
+ resizePaintArea(factor) {
1729
+ /**
1730
+ * clear canvas
1731
+ */
1732
+ this.originCanvas.width = this.originCanvas.width;
1733
+ this.displayCanvas.width = this.displayCanvas.width;
1734
+ this.drawingCanvas.width = this.drawingCanvas.width;
1735
+ this.resetLayerCanvas();
1736
+ this.nrrd_states.changedWidth = this.nrrd_states.originWidth * factor;
1737
+ this.nrrd_states.changedHeight = this.nrrd_states.originHeight * factor;
1738
+ /**
1739
+ * resize canvas
1740
+ */
1741
+ this.displayCanvas.width = this.nrrd_states.changedWidth;
1742
+ this.displayCanvas.height = this.nrrd_states.changedHeight;
1743
+ this.drawingCanvas.width = this.nrrd_states.changedWidth;
1744
+ this.drawingCanvas.height = this.nrrd_states.changedHeight;
1745
+ this.drawingCanvasLayerMaster.width = this.nrrd_states.changedWidth;
1746
+ this.drawingCanvasLayerMaster.height = this.nrrd_states.changedHeight;
1747
+ this.drawingCanvasLayerOne.width = this.nrrd_states.changedWidth;
1748
+ this.drawingCanvasLayerOne.height = this.nrrd_states.changedHeight;
1749
+ this.drawingCanvasLayerTwo.width = this.nrrd_states.changedWidth;
1750
+ this.drawingCanvasLayerTwo.height = this.nrrd_states.changedHeight;
1751
+ this.drawingCanvasLayerThree.width = this.nrrd_states.changedWidth;
1752
+ this.drawingCanvasLayerThree.height = this.nrrd_states.changedHeight;
1753
+ this.redrawDisplayCanvas();
1754
+ this.reloadMaskToLabel(this.paintImages, this.drawingLayerMasterCtx);
1755
+ this.reloadMaskToLabel(this.paintImagesLabel1, this.drawingLayerOneCtx);
1756
+ this.reloadMaskToLabel(this.paintImagesLabel2, this.drawingLayerTwoCtx);
1757
+ this.reloadMaskToLabel(this.paintImagesLabel3, this.drawingLayerTwoCtx);
1758
+ }
1759
+ /**
1760
+ * Used to init the mask on each label and reload
1761
+ * @param paintImages
1762
+ * @param ctx
1763
+ */
1764
+ reloadMaskToLabel(paintImages, ctx) {
1765
+ let paintedImage;
1766
+ switch (this.axis) {
1767
+ case "x":
1768
+ if (paintImages.x.length > 0) {
1769
+ paintedImage = this.filterDrawedImage("x", this.nrrd_states.currentIndex, paintImages);
1770
+ }
1771
+ else {
1772
+ paintedImage = undefined;
1773
+ }
1774
+ break;
1775
+ case "y":
1776
+ if (paintImages.y.length > 0) {
1777
+ paintedImage = this.filterDrawedImage("y", this.nrrd_states.currentIndex, paintImages);
1778
+ }
1779
+ else {
1780
+ paintedImage = undefined;
1781
+ }
1782
+ break;
1783
+ case "z":
1784
+ if (paintImages.z.length > 0) {
1785
+ paintedImage = this.filterDrawedImage("z", this.nrrd_states.currentIndex, paintImages);
1786
+ }
1787
+ else {
1788
+ paintedImage = undefined;
1789
+ }
1790
+ break;
1791
+ }
1792
+ if (paintedImage === null || paintedImage === void 0 ? void 0 : paintedImage.image) {
1793
+ // redraw the stored data to empty point 1
1794
+ this.setEmptyCanvasSize();
1795
+ this.emptyCtx.putImageData(paintedImage.image, 0, 0);
1796
+ ctx === null || ctx === void 0 ? void 0 : ctx.drawImage(this.emptyCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
1797
+ }
1798
+ }
1799
+ flipDisplayImageByAxis() {
1800
+ var _a, _b, _c, _d;
1801
+ if (this.axis === "x") {
1802
+ (_a = this.displayCtx) === null || _a === void 0 ? void 0 : _a.scale(-1, -1);
1803
+ (_b = this.displayCtx) === null || _b === void 0 ? void 0 : _b.translate(-this.nrrd_states.changedWidth, -this.nrrd_states.changedHeight);
1804
+ }
1805
+ else if (this.axis === "z") {
1806
+ (_c = this.displayCtx) === null || _c === void 0 ? void 0 : _c.scale(1, -1);
1807
+ (_d = this.displayCtx) === null || _d === void 0 ? void 0 : _d.translate(0, -this.nrrd_states.changedHeight);
1808
+ }
1809
+ // this.displayCtx?.scale(1, -1);
1810
+ // this.displayCtx?.translate(0, -this.nrrd_states.changedHeight);
1811
+ }
1812
+ filterDrawedImage(axis, sliceIndex, paintedImages) {
1813
+ return paintedImages[axis].filter((item) => {
1814
+ return item.index === sliceIndex;
1815
+ })[0];
1816
+ }
1817
+ // remove all folders gui controllers
1818
+ removeGuiFolderChilden(modeFolder) {
1819
+ const subControllers = modeFolder.__controllers;
1820
+ if (subControllers.length > 0)
1821
+ subControllers.forEach((c) => {
1822
+ setTimeout(() => {
1823
+ modeFolder.remove(c);
1824
+ }, 100);
1825
+ });
1826
+ }
1827
+ // Not use this function now!!!
1828
+ verifyCanvasIsEmpty(canvas) {
1829
+ this.emptyCanvas.width = canvas.width;
1830
+ this.emptyCanvas.height = canvas.height;
1831
+ const validation = canvas.toDataURL() === this.emptyCanvas.toDataURL();
1832
+ return validation;
1833
+ }
1834
+ clearDictionary(dic) {
1835
+ for (var key in dic) {
1836
+ delete dic[key];
1837
+ }
1838
+ }
1839
+ storeAllImages(index, label) {
1840
+ // const image: HTMLImageElement = new Image();
1841
+ // resize the drawing image data
1842
+ if (!this.nrrd_states.loadMaskJson) {
1843
+ this.setEmptyCanvasSize();
1844
+ this.emptyCtx.drawImage(this.drawingCanvasLayerMaster, 0, 0, this.emptyCanvas.width, this.emptyCanvas.height);
1845
+ }
1846
+ let imageData = this.emptyCtx.getImageData(0, 0, this.emptyCanvas.width, this.emptyCanvas.height);
1847
+ // 1.12.23
1848
+ switch (this.axis) {
1849
+ case "x":
1850
+ const maskData_x = this.checkSharedPlaceSlice(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, imageData);
1851
+ const marked_a_x = this.sliceArrayV(maskData_x, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_z_pixel);
1852
+ const marked_b_x = this.sliceArrayH(maskData_x, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_z_pixel);
1853
+ // const ratio_a_x =
1854
+ // this.nrrd_states.nrrd_z / this.nrrd_states.dimensions[2];
1855
+ // const ratio_b_x =
1856
+ // this.nrrd_states.nrrd_y / this.nrrd_states.dimensions[1];
1857
+ const convertXIndex = index;
1858
+ // from x the target z will replace the col pixel
1859
+ this.replaceVerticalColPixels(this.paintImages.z, this.nrrd_states.dimensions[2],
1860
+ // this.nrrd_states.ratios.z,
1861
+ 1, marked_a_x, this.nrrd_states.nrrd_x_pixel, convertXIndex);
1862
+ // from x the target y will replace the col pixel
1863
+ this.replaceVerticalColPixels(this.paintImages.y, this.nrrd_states.dimensions[1],
1864
+ // this.nrrd_states.ratios.y,
1865
+ 1, marked_b_x, this.nrrd_states.nrrd_x_pixel, convertXIndex);
1866
+ break;
1867
+ case "y":
1868
+ const maskData_y = this.checkSharedPlaceSlice(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, imageData);
1869
+ const marked_a_y = this.sliceArrayV(maskData_y, this.nrrd_states.nrrd_z_pixel, this.nrrd_states.nrrd_x_pixel);
1870
+ const marked_b_y = this.sliceArrayH(maskData_y, this.nrrd_states.nrrd_z_pixel, this.nrrd_states.nrrd_x_pixel);
1871
+ // const ratio_a_y =
1872
+ // this.nrrd_states.nrrd_x / this.nrrd_states.dimensions[0];
1873
+ // const ratio_b_y =
1874
+ // this.nrrd_states.nrrd_z / this.nrrd_states.dimensions[2];
1875
+ const convertYIndex = index;
1876
+ this.replaceHorizontalRowPixels(this.paintImages.x, this.nrrd_states.dimensions[0],
1877
+ // this.nrrd_states.ratios.x,
1878
+ 1, marked_a_y, this.nrrd_states.nrrd_z_pixel, convertYIndex);
1879
+ this.replaceHorizontalRowPixels(this.paintImages.z, this.nrrd_states.dimensions[2],
1880
+ // this.nrrd_states.ratios.z,
1881
+ 1, marked_b_y, this.nrrd_states.nrrd_x_pixel, convertYIndex);
1882
+ break;
1883
+ case "z":
1884
+ // for x slices get cols' pixels
1885
+ // for y slices get rows' pixels
1886
+ // 1. slice z 的 y轴对应了slice y的index,所以我们可以通过slice z 确定在y轴上那些行是有pixels的,我们就可以将它的y坐标(或者是行号)对应到slice y的index,并将该index下的marked image提取出来。
1887
+ // 2. 接着我们可以通过当前slice z 的index,来确定marked image 需要替换或重组的 行 pixel array。
1888
+ const maskData_z = this.checkSharedPlaceSlice(this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel, imageData);
1889
+ // 1. get slice z's each row's and col's pixel as a 2d array.
1890
+ // 1.1 get the cols' 2d array for slice x
1891
+ const marked_a_z = this.sliceArrayV(maskData_z, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_x_pixel);
1892
+ // 1.2 get the rows' 2d array for slice y
1893
+ const marked_b_z = this.sliceArrayH(maskData_z, this.nrrd_states.nrrd_y_pixel, this.nrrd_states.nrrd_x_pixel);
1894
+ // 1.3 get x axis ratio for converting, to match the number slice x with the slice z's x axis pixel number.
1895
+ // const ratio_a_z =
1896
+ // this.nrrd_states.nrrd_x / this.nrrd_states.dimensions[0];
1897
+ // // 1.4 get y axis ratio for converting
1898
+ // const ratio_b_z =
1899
+ // this.nrrd_states.nrrd_y / this.nrrd_states.dimensions[1];
1900
+ // 1.5 To identify which row/col data should be replace
1901
+ const convertZIndex = index;
1902
+ // 2. Mapping coordinates
1903
+ // from z the target x will replace the col pixel
1904
+ this.replaceVerticalColPixels(this.paintImages.x, this.nrrd_states.dimensions[0],
1905
+ // this.nrrd_states.ratios.x,
1906
+ 1, marked_a_z, this.nrrd_states.nrrd_z_pixel, convertZIndex);
1907
+ // from z the target y will replace row pixel
1908
+ this.replaceHorizontalRowPixels(this.paintImages.y, this.nrrd_states.dimensions[1],
1909
+ // this.nrrd_states.ratios.y,
1910
+ 1, marked_b_z, this.nrrd_states.nrrd_x_pixel, convertZIndex);
1911
+ break;
1912
+ }
1913
+ this.storeImageToAxis(index, this.paintImages, imageData);
1914
+ if (!this.nrrd_states.loadMaskJson) {
1915
+ this.storeEachLayerImage(index, label);
1916
+ }
1917
+ }
1918
+ storeImageToAxis(index, paintedImages, imageData) {
1919
+ var _a, _b, _c;
1920
+ let temp = {
1921
+ index,
1922
+ image: imageData,
1923
+ };
1924
+ let drawedImage;
1925
+ switch (this.axis) {
1926
+ case "x":
1927
+ drawedImage = this.filterDrawedImage("x", index, paintedImages);
1928
+ drawedImage
1929
+ ? (drawedImage.image = imageData)
1930
+ : (_a = paintedImages.x) === null || _a === void 0 ? void 0 : _a.push(temp);
1931
+ break;
1932
+ case "y":
1933
+ drawedImage = this.filterDrawedImage("y", index, paintedImages);
1934
+ drawedImage
1935
+ ? (drawedImage.image = imageData)
1936
+ : (_b = paintedImages.y) === null || _b === void 0 ? void 0 : _b.push(temp);
1937
+ break;
1938
+ case "z":
1939
+ drawedImage = this.filterDrawedImage("z", index, paintedImages);
1940
+ drawedImage
1941
+ ? (drawedImage.image = imageData)
1942
+ : (_c = paintedImages.z) === null || _c === void 0 ? void 0 : _c.push(temp);
1943
+ break;
1944
+ }
1945
+ }
1946
+ storeImageToLabel(index, canvas, paintedImages) {
1947
+ if (!this.nrrd_states.loadMaskJson) {
1948
+ this.setEmptyCanvasSize();
1949
+ this.emptyCtx.drawImage(canvas, 0, 0, this.emptyCanvas.width, this.emptyCanvas.height);
1950
+ }
1951
+ const imageData = this.emptyCtx.getImageData(0, 0, this.emptyCanvas.width, this.emptyCanvas.height);
1952
+ this.storeImageToAxis(index, paintedImages, imageData);
1953
+ // this.setEmptyCanvasSize()
1954
+ return imageData;
1955
+ }
1956
+ storeEachLayerImage(index, label) {
1957
+ if (!this.nrrd_states.loadMaskJson) {
1958
+ this.setEmptyCanvasSize();
1959
+ }
1960
+ let imageData;
1961
+ switch (label) {
1962
+ case "label1":
1963
+ imageData = this.storeImageToLabel(index, this.drawingCanvasLayerOne, this.paintImagesLabel1);
1964
+ break;
1965
+ case "label2":
1966
+ imageData = this.storeImageToLabel(index, this.drawingCanvasLayerTwo, this.paintImagesLabel2);
1967
+ break;
1968
+ case "label3":
1969
+ imageData = this.storeImageToLabel(index, this.drawingCanvasLayerThree, this.paintImagesLabel3);
1970
+ break;
1971
+ }
1972
+ // callback function to return the painted image
1973
+ if (!this.nrrd_states.loadMaskJson && this.axis == "z") {
1974
+ 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);
1975
+ }
1976
+ }
1977
+ // slice array to 2d array
1978
+ sliceArrayH(arr, row, col) {
1979
+ const arr2D = [];
1980
+ for (let i = 0; i < row; i++) {
1981
+ const start = i * col * 4;
1982
+ const end = (i + 1) * col * 4;
1983
+ const temp = arr.slice(start, end);
1984
+ arr2D.push(temp);
1985
+ }
1986
+ return arr2D;
1987
+ }
1988
+ sliceArrayV(arr, row, col) {
1989
+ const arr2D = [];
1990
+ const base = col * 4;
1991
+ for (let i = 0; i < col; i++) {
1992
+ const temp = [];
1993
+ for (let j = 0; j < row; j++) {
1994
+ const index = base * j + i * 4;
1995
+ temp.push(arr[index]);
1996
+ temp.push(arr[index + 1]);
1997
+ temp.push(arr[index + 2]);
1998
+ temp.push(arr[index + 3]);
1999
+ }
2000
+ arr2D.push(temp);
2001
+ }
2002
+ return arr2D;
2003
+ }
2004
+ /**
2005
+ *
2006
+ * @param paintImageArray : the target view slice's marked images array
2007
+ * @param length : the target view slice's dimention (total slice index num)
2008
+ * @param ratio : the target slice image's width/height ratio of its dimention length
2009
+ * @param markedArr : current painted image's vertical 2d Array
2010
+ * @param targetWidth : the target image width
2011
+ * @param convertIndex : Mapping current image's index to target slice image's width/height pixel start point
2012
+ */
2013
+ replaceVerticalColPixels(paintImageArray, length, ratio, markedArr, targetWidth, convertIndex) {
2014
+ for (let i = 0, len = length; i < len; i++) {
2015
+ const index = Math.floor(i * ratio);
2016
+ const convertImageArray = paintImageArray[i].image.data;
2017
+ const mark_data = markedArr[index];
2018
+ const base_a = targetWidth * 4;
2019
+ for (let j = 0, len = mark_data.length; j < len; j += 4) {
2020
+ const start = (j / 4) * base_a + convertIndex * 4;
2021
+ convertImageArray[start] = mark_data[j];
2022
+ convertImageArray[start + 1] = mark_data[j + 1];
2023
+ convertImageArray[start + 2] = mark_data[j + 2];
2024
+ convertImageArray[start + 3] = mark_data[j + 3];
2025
+ }
2026
+ }
2027
+ }
2028
+ /**
2029
+ *
2030
+ * @param paintImageArray : the target view slice's marked images array
2031
+ * @param length : the target view slice's dimention (total slice index num)
2032
+ * @param ratio : the target slice image's width/height ratio of its dimention length
2033
+ * @param markedArr : current painted image's horizontal 2d Array
2034
+ * @param targetWidth : the target image width
2035
+ * @param convertIndex : Mapping current image's index to target slice image's width/height pixel start point
2036
+ */
2037
+ replaceHorizontalRowPixels(paintImageArray, length, ratio, markedArr, targetWidth, convertIndex) {
2038
+ for (let i = 0, len = length; i < len; i++) {
2039
+ const index = Math.floor(i * ratio);
2040
+ const convertImageArray = paintImageArray[i].image.data;
2041
+ const mark_data = markedArr[index];
2042
+ const start = targetWidth * convertIndex * 4;
2043
+ for (let j = 0, len = mark_data.length; j < len; j++) {
2044
+ convertImageArray[start + j] = mark_data[j];
2045
+ }
2046
+ }
2047
+ }
2048
+ // set the empty canvas width and height, to reduce duplicate codes
2049
+ setEmptyCanvasSize() {
2050
+ switch (this.axis) {
2051
+ case "x":
2052
+ this.emptyCanvas.width = this.nrrd_states.nrrd_z_pixel;
2053
+ this.emptyCanvas.height = this.nrrd_states.nrrd_y_pixel;
2054
+ break;
2055
+ case "y":
2056
+ this.emptyCanvas.width = this.nrrd_states.nrrd_x_pixel;
2057
+ this.emptyCanvas.height = this.nrrd_states.nrrd_z_pixel;
2058
+ break;
2059
+ case "z":
2060
+ this.emptyCanvas.width = this.nrrd_states.nrrd_x_pixel;
2061
+ this.emptyCanvas.height = this.nrrd_states.nrrd_y_pixel;
2062
+ break;
2063
+ }
2064
+ }
2065
+ checkSharedPlaceSlice(width, height, imageData) {
2066
+ let maskData = this.emptyCtx.createImageData(width, height).data;
2067
+ if (this.nrrd_states.sharedPlace.z.includes(this.nrrd_states.currentIndex)) {
2068
+ const sharedPlaceArr = this.findSliceInSharedPlace();
2069
+ sharedPlaceArr.push(imageData);
2070
+ if (sharedPlaceArr.length > 0) {
2071
+ for (let i = 0; i < sharedPlaceArr.length; i++) {
2072
+ this.replaceArray(maskData, sharedPlaceArr[i].data);
2073
+ }
2074
+ }
2075
+ }
2076
+ else {
2077
+ maskData = imageData.data;
2078
+ }
2079
+ return maskData;
2080
+ }
2081
+ // replace Array
2082
+ replaceArray(mainArr, replaceArr) {
2083
+ for (let i = 0, len = replaceArr.length; i < len; i++) {
2084
+ if (replaceArr[i] === 0 || mainArr[i] !== 0) {
2085
+ continue;
2086
+ }
2087
+ else {
2088
+ mainArr[i] = replaceArr[i];
2089
+ }
2090
+ }
2091
+ }
2092
+ findSliceInSharedPlace() {
2093
+ const sharedPlaceImages = [];
2094
+ const base = Math.floor(this.nrrd_states.currentIndex * this.nrrd_states.ratios[this.axis]);
2095
+ for (let i = 1; i <= 3; i++) {
2096
+ const index = this.nrrd_states.currentIndex - i;
2097
+ if (index < this.nrrd_states.minIndex) {
2098
+ break;
2099
+ }
2100
+ else {
2101
+ const newIndex = Math.floor(index * this.nrrd_states.ratios[this.axis]);
2102
+ if (newIndex === base) {
2103
+ sharedPlaceImages.push(this.paintImages[this.axis][index].image);
2104
+ }
2105
+ }
2106
+ }
2107
+ for (let i = 1; i <= 3; i++) {
2108
+ const index = this.nrrd_states.currentIndex + i;
2109
+ if (index > this.nrrd_states.maxIndex) {
2110
+ break;
2111
+ }
2112
+ else {
2113
+ const newIndex = Math.floor(index * this.nrrd_states.ratios[this.axis]);
2114
+ if (newIndex === base) {
2115
+ sharedPlaceImages.push(this.paintImages[this.axis][index].image);
2116
+ }
2117
+ }
2118
+ }
2119
+ return sharedPlaceImages;
2120
+ }
2121
+ exportData() {
2122
+ let exportDataFormat = { x: [], y: [], z: [] };
2123
+ // exportDataFormat.x = this.restructData(
2124
+ // this.paintImages.x,
2125
+ // this.paintImages.x.length
2126
+ // );
2127
+ // exportDataFormat.y = this.restructData(
2128
+ // this.paintImages.y,
2129
+ // this.paintImages.y.length
2130
+ // );
2131
+ // const worker = new Worker(
2132
+ // new URL("./workers/reformatSaveDataWorker.ts", import.meta.url),
2133
+ // {
2134
+ // type: "module",
2135
+ // }
2136
+ // );
2137
+ window.alert("Export masks, starting!!!");
2138
+ const masks = restructData(this.paintImages.z, this.nrrd_states.nrrd_z_pixel, this.nrrd_states.nrrd_x_pixel, this.nrrd_states.nrrd_y_pixel);
2139
+ const blob = convertReformatDataToBlob(masks);
2140
+ if (blob) {
2141
+ saveFileAsJson(blob, "copper3D_export data_z.json");
2142
+ window.alert("Export masks successfully!!!");
2143
+ }
2144
+ else {
2145
+ window.alert("Export failed!");
2146
+ }
2147
+ // worker.postMessage({
2148
+ // masksData: this.paintImages.z,
2149
+ // len: this.paintImages.z.length,
2150
+ // width: this.nrrd_states.nrrd_x_pixel,
2151
+ // height: this.nrrd_states.nrrd_y_pixel,
2152
+ // type: "reformat",
2153
+ // });
2154
+ // worker.onmessage = (ev: MessageEvent) => {
2155
+ // const result = ev.data;
2156
+ // if (result.type === "reformat") {
2157
+ // exportDataFormat.z = result.masks;
2158
+ // worker.postMessage({
2159
+ // masksData: exportDataFormat.z,
2160
+ // type: "saveBlob",
2161
+ // });
2162
+ // } else if (result.type === "saveBlob") {
2163
+ // if (result.data) {
2164
+ // saveFileAsJson(result.data, "copper3D_export data_z.json");
2165
+ // window.alert("Export masks successfully!!!");
2166
+ // } else {
2167
+ // window.alert("Export failed!");
2168
+ // }
2169
+ // }
2170
+ // };
2171
+ }
2172
+ }
2173
+ //# sourceMappingURL=nrrd_tool.js.map