copper3d 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Utils/segmentation/CommToolsData.d.ts +9 -10
- package/dist/Utils/segmentation/CommToolsData.js +4 -13
- package/dist/Utils/segmentation/CommToolsData.js.map +1 -1
- package/dist/Utils/segmentation/DragOperator.js +4 -4
- package/dist/Utils/segmentation/DragOperator.js.map +1 -1
- package/dist/Utils/segmentation/DrawToolCore.d.ts +5 -6
- package/dist/Utils/segmentation/DrawToolCore.js +57 -55
- package/dist/Utils/segmentation/DrawToolCore.js.map +1 -1
- package/dist/Utils/segmentation/NrrdTools.js +6 -2
- package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
- package/dist/Utils/segmentation/core/MaskVolume.d.ts +0 -33
- package/dist/Utils/segmentation/core/MaskVolume.js +0 -121
- package/dist/Utils/segmentation/core/MaskVolume.js.map +1 -1
- package/dist/Utils/segmentation/coreTools/coreType.d.ts +0 -3
- package/dist/Utils/segmentation/coreTools/coreType.js.map +1 -1
- package/dist/Utils/segmentation/coreTools/gui.d.ts +6 -3
- package/dist/Utils/segmentation/coreTools/gui.js.map +1 -1
- package/dist/Utils/segmentation/eventRouter/EventRouter.d.ts +1 -0
- package/dist/Utils/segmentation/eventRouter/EventRouter.js +9 -3
- package/dist/Utils/segmentation/eventRouter/EventRouter.js.map +1 -1
- package/dist/Utils/segmentation/tools/BaseTool.d.ts +3 -0
- package/dist/Utils/segmentation/tools/BaseTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/ImageStoreHelper.d.ts +10 -13
- package/dist/Utils/segmentation/tools/ImageStoreHelper.js +13 -27
- package/dist/Utils/segmentation/tools/ImageStoreHelper.js.map +1 -1
- package/dist/Utils/segmentation/tools/SphereTool.d.ts +1 -3
- package/dist/Utils/segmentation/tools/SphereTool.js +1 -1
- package/dist/Utils/segmentation/tools/SphereTool.js.map +1 -1
- package/dist/Utils/segmentation/tools/ZoomTool.js +2 -1
- package/dist/Utils/segmentation/tools/ZoomTool.js.map +1 -1
- package/dist/bundle.esm.js +97 -228
- package/dist/bundle.umd.js +97 -228
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types/Utils/segmentation/CommToolsData.d.ts +9 -10
- package/dist/types/Utils/segmentation/DrawToolCore.d.ts +5 -6
- package/dist/types/Utils/segmentation/core/MaskVolume.d.ts +0 -33
- package/dist/types/Utils/segmentation/coreTools/coreType.d.ts +0 -3
- package/dist/types/Utils/segmentation/coreTools/gui.d.ts +6 -3
- package/dist/types/Utils/segmentation/eventRouter/EventRouter.d.ts +1 -0
- package/dist/types/Utils/segmentation/tools/BaseTool.d.ts +3 -0
- package/dist/types/Utils/segmentation/tools/ImageStoreHelper.d.ts +10 -13
- package/dist/types/Utils/segmentation/tools/SphereTool.d.ts +1 -3
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/bundle.umd.js
CHANGED
|
@@ -71146,127 +71146,6 @@ void main() {
|
|
|
71146
71146
|
}
|
|
71147
71147
|
}
|
|
71148
71148
|
}
|
|
71149
|
-
/**
|
|
71150
|
-
* Extract a 2D slice as raw RGBA ImageData (lossless round-trip).
|
|
71151
|
-
*
|
|
71152
|
-
* Requires the volume to have **≥ 4 channels** (ch0=R, ch1=G, ch2=B, ch3=A).
|
|
71153
|
-
* Returns the exact pixel data that was stored via `setSliceFromImageData`,
|
|
71154
|
-
* bypassing all render modes.
|
|
71155
|
-
*
|
|
71156
|
-
* @param sliceIndex Index along the specified axis.
|
|
71157
|
-
* @param axis `'x'`, `'y'`, or `'z'` (default `'z'`).
|
|
71158
|
-
* @returns ImageData with the stored RGBA values.
|
|
71159
|
-
*
|
|
71160
|
-
* @throws {RangeError} If sliceIndex is out of bounds.
|
|
71161
|
-
* @throws {Error} If the volume has fewer than 4 channels.
|
|
71162
|
-
*/
|
|
71163
|
-
getSliceRawImageData(sliceIndex, axis = 'z') {
|
|
71164
|
-
if (this.numChannels < 4) {
|
|
71165
|
-
throw new Error(`getSliceRawImageData requires ≥ 4 channels, volume has ${this.numChannels}`);
|
|
71166
|
-
}
|
|
71167
|
-
this.validateSliceIndex(sliceIndex, axis);
|
|
71168
|
-
const { width, height } = this.dims;
|
|
71169
|
-
const ch = this.numChannels;
|
|
71170
|
-
const [sliceWidth, sliceHeight] = this.getSliceDimensions(axis);
|
|
71171
|
-
const imageData = new ImageData(sliceWidth, sliceHeight);
|
|
71172
|
-
const pixels = imageData.data;
|
|
71173
|
-
const volData = this.data;
|
|
71174
|
-
if (axis === 'z') {
|
|
71175
|
-
// Z-axis (axial): slice data is contiguous in memory — direct bulk copy
|
|
71176
|
-
const offset = sliceIndex * this.bytesPerSlice;
|
|
71177
|
-
pixels.set(volData.subarray(offset, offset + this.bytesPerSlice));
|
|
71178
|
-
}
|
|
71179
|
-
else if (axis === 'y') {
|
|
71180
|
-
// Y-axis (coronal): each row (fixed z, fixed y, varying x) is contiguous
|
|
71181
|
-
// mapping: (i → x, sliceIndex → y, j → z)
|
|
71182
|
-
const rowBytes = width * ch;
|
|
71183
|
-
let px = 0;
|
|
71184
|
-
for (let j = 0; j < sliceHeight; j++) {
|
|
71185
|
-
const rowStart = j * height * width * ch + sliceIndex * width * ch;
|
|
71186
|
-
pixels.set(volData.subarray(rowStart, rowStart + rowBytes), px);
|
|
71187
|
-
px += rowBytes;
|
|
71188
|
-
}
|
|
71189
|
-
}
|
|
71190
|
-
else {
|
|
71191
|
-
// X-axis (sagittal): pixels not contiguous, but avoid function call overhead
|
|
71192
|
-
// After dimension fix: sliceWidth = depth (Z), sliceHeight = height (Y)
|
|
71193
|
-
// j → y (rows), i → z (columns)
|
|
71194
|
-
let px = 0;
|
|
71195
|
-
for (let j = 0; j < sliceHeight; j++) {
|
|
71196
|
-
const yOffset = j * width * ch;
|
|
71197
|
-
for (let i = 0; i < sliceWidth; i++) {
|
|
71198
|
-
const baseIdx = i * height * width * ch + yOffset + sliceIndex * ch;
|
|
71199
|
-
pixels[px] = volData[baseIdx];
|
|
71200
|
-
pixels[px + 1] = volData[baseIdx + 1];
|
|
71201
|
-
pixels[px + 2] = volData[baseIdx + 2];
|
|
71202
|
-
pixels[px + 3] = volData[baseIdx + 3];
|
|
71203
|
-
px += 4;
|
|
71204
|
-
}
|
|
71205
|
-
}
|
|
71206
|
-
}
|
|
71207
|
-
return imageData;
|
|
71208
|
-
}
|
|
71209
|
-
/**
|
|
71210
|
-
* Write slice data into an existing ImageData buffer (zero-allocation).
|
|
71211
|
-
*
|
|
71212
|
-
* Same semantics as {@link getSliceRawImageData} but avoids creating a new
|
|
71213
|
-
* ImageData object on every call. The caller is responsible for providing
|
|
71214
|
-
* a buffer whose dimensions match the expected slice size.
|
|
71215
|
-
*
|
|
71216
|
-
* All pixels are fully overwritten — no clearing is needed beforehand.
|
|
71217
|
-
*
|
|
71218
|
-
* @param sliceIndex Index along the specified axis.
|
|
71219
|
-
* @param axis `'x'`, `'y'`, or `'z'` (default `'z'`).
|
|
71220
|
-
* @param target Pre-allocated ImageData to write into.
|
|
71221
|
-
*
|
|
71222
|
-
* @throws {Error} If the volume has fewer than 4 channels.
|
|
71223
|
-
* @throws {RangeError} If sliceIndex is out of bounds.
|
|
71224
|
-
* @throws {Error} If target dimensions don't match the slice.
|
|
71225
|
-
*/
|
|
71226
|
-
getSliceRawImageDataInto(sliceIndex, axis = 'z', target) {
|
|
71227
|
-
if (this.numChannels < 4) {
|
|
71228
|
-
throw new Error(`getSliceRawImageDataInto requires ≥ 4 channels, volume has ${this.numChannels}`);
|
|
71229
|
-
}
|
|
71230
|
-
this.validateSliceIndex(sliceIndex, axis);
|
|
71231
|
-
const [sliceWidth, sliceHeight] = this.getSliceDimensions(axis);
|
|
71232
|
-
if (target.width !== sliceWidth || target.height !== sliceHeight) {
|
|
71233
|
-
throw new Error(`Buffer size mismatch: expected ${sliceWidth}×${sliceHeight}, ` +
|
|
71234
|
-
`got ${target.width}×${target.height}`);
|
|
71235
|
-
}
|
|
71236
|
-
const { width, height } = this.dims;
|
|
71237
|
-
const ch = this.numChannels;
|
|
71238
|
-
const pixels = target.data;
|
|
71239
|
-
const volData = this.data;
|
|
71240
|
-
if (axis === 'z') {
|
|
71241
|
-
const offset = sliceIndex * this.bytesPerSlice;
|
|
71242
|
-
pixels.set(volData.subarray(offset, offset + this.bytesPerSlice));
|
|
71243
|
-
}
|
|
71244
|
-
else if (axis === 'y') {
|
|
71245
|
-
const rowBytes = width * ch;
|
|
71246
|
-
let px = 0;
|
|
71247
|
-
for (let j = 0; j < sliceHeight; j++) {
|
|
71248
|
-
const rowStart = j * height * width * ch + sliceIndex * width * ch;
|
|
71249
|
-
pixels.set(volData.subarray(rowStart, rowStart + rowBytes), px);
|
|
71250
|
-
px += rowBytes;
|
|
71251
|
-
}
|
|
71252
|
-
}
|
|
71253
|
-
else {
|
|
71254
|
-
// X-axis (sagittal): sliceWidth = depth (Z), sliceHeight = height (Y)
|
|
71255
|
-
// j → y (rows), i → z (columns)
|
|
71256
|
-
let px = 0;
|
|
71257
|
-
for (let j = 0; j < sliceHeight; j++) {
|
|
71258
|
-
const yOffset = j * width * ch;
|
|
71259
|
-
for (let i = 0; i < sliceWidth; i++) {
|
|
71260
|
-
const baseIdx = i * height * width * ch + yOffset + sliceIndex * ch;
|
|
71261
|
-
pixels[px] = volData[baseIdx];
|
|
71262
|
-
pixels[px + 1] = volData[baseIdx + 1];
|
|
71263
|
-
pixels[px + 2] = volData[baseIdx + 2];
|
|
71264
|
-
pixels[px + 3] = volData[baseIdx + 3];
|
|
71265
|
-
px += 4;
|
|
71266
|
-
}
|
|
71267
|
-
}
|
|
71268
|
-
}
|
|
71269
|
-
}
|
|
71270
71149
|
// ── Label-based storage (1-channel volumes) ────────────────────────
|
|
71271
71150
|
/**
|
|
71272
71151
|
/**
|
|
@@ -72789,12 +72668,12 @@ void main() {
|
|
|
72789
72668
|
this.eventRouter.subscribeModeChange((prevMode, newMode) => {
|
|
72790
72669
|
const prev = prevMode;
|
|
72791
72670
|
const next = newMode;
|
|
72792
|
-
// When entering draw or
|
|
72793
|
-
if (next === 'draw' || next === 'contrast') {
|
|
72671
|
+
// When entering draw, contrast, or crosshair mode, remove drag mode
|
|
72672
|
+
if (next === 'draw' || next === 'contrast' || next === 'crosshair') {
|
|
72794
72673
|
this.removeDragMode();
|
|
72795
72674
|
}
|
|
72796
|
-
// When leaving draw or
|
|
72797
|
-
if ((prev === 'draw' || prev === 'contrast') && next === 'idle') {
|
|
72675
|
+
// When leaving draw, contrast, or crosshair mode (returning to idle), restore drag mode
|
|
72676
|
+
if ((prev === 'draw' || prev === 'contrast' || prev === 'crosshair') && next === 'idle') {
|
|
72798
72677
|
if (!this.gui_states.sphere) {
|
|
72799
72678
|
this.configDragMode();
|
|
72800
72679
|
}
|
|
@@ -72897,7 +72776,6 @@ void main() {
|
|
|
72897
72776
|
ratios: { x: 1, y: 1, z: 1 },
|
|
72898
72777
|
contrastNum: 0,
|
|
72899
72778
|
showContrast: false,
|
|
72900
|
-
enableCursorChoose: false,
|
|
72901
72779
|
isCursorSelect: false,
|
|
72902
72780
|
cursorPageX: 0,
|
|
72903
72781
|
cursorPageY: 0,
|
|
@@ -73037,8 +72915,6 @@ void main() {
|
|
|
73037
72915
|
skipSlicesDic: {},
|
|
73038
72916
|
currentShowingSlice: undefined,
|
|
73039
72917
|
mainPreSlices: undefined,
|
|
73040
|
-
Is_Shift_Pressed: false,
|
|
73041
|
-
Is_Ctrl_Pressed: false,
|
|
73042
72918
|
Is_Draw: false,
|
|
73043
72919
|
axis: "z",
|
|
73044
72920
|
maskData: {
|
|
@@ -73188,12 +73064,6 @@ void main() {
|
|
|
73188
73064
|
clearStoreImages() {
|
|
73189
73065
|
throw new Error("Child class must implement abstract clearStoreImages, currently you can find it in NrrdTools.");
|
|
73190
73066
|
}
|
|
73191
|
-
/**
|
|
73192
|
-
* Rewrite this {createEmptyPaintImage} function under NrrdTools
|
|
73193
|
-
*/
|
|
73194
|
-
createEmptyPaintImage(dimensions, paintImages) {
|
|
73195
|
-
throw new Error("Child class must implement abstract clearStoreImages, currently you can find it in NrrdTools.");
|
|
73196
|
-
}
|
|
73197
73067
|
/**
|
|
73198
73068
|
* Rewrite this {resizePaintArea} function under NrrdTools
|
|
73199
73069
|
*/
|
|
@@ -73255,13 +73125,13 @@ void main() {
|
|
|
73255
73125
|
throw new Error("Child class must implement abstract redrawDisplayCanvas, currently you can find it in NrrdTools.");
|
|
73256
73126
|
}
|
|
73257
73127
|
/**
|
|
73258
|
-
* Get a painted mask image
|
|
73128
|
+
* Get a painted mask image based on current axis and input slice index.
|
|
73259
73129
|
*
|
|
73260
|
-
* Phase 3: Reads directly from MaskVolume
|
|
73130
|
+
* Phase 3: Reads directly from MaskVolume.
|
|
73261
73131
|
*
|
|
73262
73132
|
* @param axis "x" | "y" | "z"
|
|
73263
73133
|
* @param sliceIndex number
|
|
73264
|
-
* @returns
|
|
73134
|
+
* @returns Object with index and image, or undefined
|
|
73265
73135
|
*/
|
|
73266
73136
|
filterDrawedImage(axis, sliceIndex) {
|
|
73267
73137
|
try {
|
|
@@ -73318,7 +73188,7 @@ void main() {
|
|
|
73318
73188
|
/**
|
|
73319
73189
|
* Render a layer's slice into a reusable buffer and draw to the target canvas.
|
|
73320
73190
|
*
|
|
73321
|
-
* Uses MaskVolume.
|
|
73191
|
+
* Uses MaskVolume.renderLabelSliceInto() for zero-allocation rendering.
|
|
73322
73192
|
* The caller should obtain the buffer via getOrCreateSliceBuffer() and reuse
|
|
73323
73193
|
* it across multiple layer renders.
|
|
73324
73194
|
*
|
|
@@ -73574,11 +73444,13 @@ void main() {
|
|
|
73574
73444
|
}
|
|
73575
73445
|
/**
|
|
73576
73446
|
* Toggle crosshair mode.
|
|
73447
|
+
* Blocked when draw or contrast mode is active (mutual exclusion).
|
|
73577
73448
|
*/
|
|
73578
73449
|
toggleCrosshair() {
|
|
73579
73450
|
if (!DRAWING_TOOLS.has(this.guiTool))
|
|
73580
73451
|
return;
|
|
73581
|
-
|
|
73452
|
+
// Block crosshair activation during draw or contrast
|
|
73453
|
+
if (this.state.shiftHeld || this.mode === 'draw' || this.mode === 'contrast')
|
|
73582
73454
|
return;
|
|
73583
73455
|
this.state.crosshairEnabled = !this.state.crosshairEnabled;
|
|
73584
73456
|
this.setMode(this.state.crosshairEnabled ? 'crosshair' : 'idle');
|
|
@@ -73697,12 +73569,16 @@ void main() {
|
|
|
73697
73569
|
// Update state based on modifier keys
|
|
73698
73570
|
if (ev.key === this.keyboardSettings.draw) {
|
|
73699
73571
|
this.state.shiftHeld = true;
|
|
73700
|
-
|
|
73572
|
+
// Block draw mode when crosshair or contrast is active (mutual exclusion)
|
|
73573
|
+
if (DRAWING_TOOLS.has(this.guiTool) && !this.state.ctrlHeld && !this.state.crosshairEnabled) {
|
|
73701
73574
|
this.setMode('draw');
|
|
73702
73575
|
}
|
|
73703
73576
|
}
|
|
73704
73577
|
if (this.contrastEnabled && this.keyboardSettings.contrast.includes(ev.key)) {
|
|
73705
|
-
|
|
73578
|
+
// Block contrast state when crosshair or draw is active (mutual exclusion)
|
|
73579
|
+
if (!this.state.crosshairEnabled && this.mode !== 'draw') {
|
|
73580
|
+
this.state.ctrlHeld = true;
|
|
73581
|
+
}
|
|
73706
73582
|
}
|
|
73707
73583
|
// Route to external handler
|
|
73708
73584
|
if (this.keydownHandler) {
|
|
@@ -73862,7 +73738,7 @@ void main() {
|
|
|
73862
73738
|
this.callbacks.setEmptyCanvasSize(axis);
|
|
73863
73739
|
this.callbacks.drawImageOnEmptyImage(this.ctx.protectedData.canvases.drawingSphereCanvas);
|
|
73864
73740
|
const imageData = this.ctx.protectedData.ctxes.emptyCtx.getImageData(0, 0, this.ctx.protectedData.canvases.emptyCanvas.width, this.ctx.protectedData.canvases.emptyCanvas.height);
|
|
73865
|
-
this.callbacks.storeImageToAxis(index,
|
|
73741
|
+
this.callbacks.storeImageToAxis(index, imageData, axis);
|
|
73866
73742
|
}
|
|
73867
73743
|
// ===== Multi-View Sphere =====
|
|
73868
73744
|
drawSphereOnEachViews(decay, axis) {
|
|
@@ -74259,7 +74135,8 @@ void main() {
|
|
|
74259
74135
|
configMouseZoomWheel() {
|
|
74260
74136
|
let moveDistance = 1;
|
|
74261
74137
|
return (e) => {
|
|
74262
|
-
|
|
74138
|
+
var _a;
|
|
74139
|
+
if ((_a = this.ctx.eventRouter) === null || _a === void 0 ? void 0 : _a.isShiftHeld()) {
|
|
74263
74140
|
return;
|
|
74264
74141
|
}
|
|
74265
74142
|
e.preventDefault();
|
|
@@ -74396,8 +74273,7 @@ void main() {
|
|
|
74396
74273
|
* Extracted from DrawToolCore.ts:
|
|
74397
74274
|
* - storeAllImages / storeImageToAxis / storeImageToLayer / storeEachLayerImage
|
|
74398
74275
|
*
|
|
74399
|
-
* Phase
|
|
74400
|
-
* Volume is the primary storage; IPaintImages kept for backward compatibility.
|
|
74276
|
+
* Phase 3: MaskVolume is the sole storage backend. All IPaintImages params removed.
|
|
74401
74277
|
*/
|
|
74402
74278
|
class ImageStoreHelper extends BaseTool {
|
|
74403
74279
|
constructor(ctx, callbacks) {
|
|
@@ -74414,12 +74290,12 @@ void main() {
|
|
|
74414
74290
|
*/
|
|
74415
74291
|
getVolumeForLayer(layer) {
|
|
74416
74292
|
const { volumes } = this.ctx.protectedData.maskData;
|
|
74417
|
-
|
|
74418
|
-
|
|
74419
|
-
|
|
74420
|
-
|
|
74421
|
-
|
|
74422
|
-
|
|
74293
|
+
const vol = volumes[layer];
|
|
74294
|
+
if (vol)
|
|
74295
|
+
return vol;
|
|
74296
|
+
const firstLayerId = this.ctx.nrrd_states.layers[0];
|
|
74297
|
+
console.warn(`ImageStoreHelper: unknown layer "${layer}", falling back to "${firstLayerId}"`);
|
|
74298
|
+
return volumes[firstLayerId];
|
|
74423
74299
|
}
|
|
74424
74300
|
/**
|
|
74425
74301
|
* Get MaskVolume for the currently active layer.
|
|
@@ -74438,19 +74314,17 @@ void main() {
|
|
|
74438
74314
|
}
|
|
74439
74315
|
// ===== Store Image To Axis =====
|
|
74440
74316
|
/**
|
|
74441
|
-
* Phase 3:
|
|
74442
|
-
* MaskVolume storage happens in storeAllImages via setSliceFromImageData.
|
|
74443
|
-
* This method kept for backward compatibility with existing call sites.
|
|
74317
|
+
* Phase 3: No-op — MaskVolume storage happens in storeAllImages.
|
|
74444
74318
|
*/
|
|
74445
|
-
storeImageToAxis(_index,
|
|
74319
|
+
storeImageToAxis(_index, _imageData, _axis) {
|
|
74446
74320
|
// No-op: MaskVolume is the primary storage, updated in storeAllImages
|
|
74447
74321
|
}
|
|
74448
74322
|
/**
|
|
74449
74323
|
* Retrieve the drawn image for a given axis and slice.
|
|
74450
74324
|
*
|
|
74451
|
-
* Phase 3: Reads exclusively from MaskVolume
|
|
74325
|
+
* Phase 3: Reads exclusively from MaskVolume.
|
|
74452
74326
|
*/
|
|
74453
|
-
filterDrawedImage(axis, sliceIndex
|
|
74327
|
+
filterDrawedImage(axis, sliceIndex) {
|
|
74454
74328
|
try {
|
|
74455
74329
|
const volume = this.getCurrentVolume();
|
|
74456
74330
|
if (volume) {
|
|
@@ -74539,10 +74413,9 @@ void main() {
|
|
|
74539
74413
|
}
|
|
74540
74414
|
}
|
|
74541
74415
|
/**
|
|
74542
|
-
*
|
|
74543
|
-
* Kept for backward compatibility with existing call sites.
|
|
74416
|
+
* Extract ImageData from canvas (MaskVolume storage is handled in storeAllImages).
|
|
74544
74417
|
*/
|
|
74545
|
-
storeImageToLayer(_index, canvas
|
|
74418
|
+
storeImageToLayer(_index, canvas) {
|
|
74546
74419
|
if (!this.ctx.nrrd_states.loadMaskJson) {
|
|
74547
74420
|
this.callbacks.setEmptyCanvasSize();
|
|
74548
74421
|
this.callbacks.drawImageOnEmptyImage(canvas);
|
|
@@ -74551,16 +74424,6 @@ void main() {
|
|
|
74551
74424
|
// No longer stores to paintedImages - MaskVolume is primary storage
|
|
74552
74425
|
return imageData;
|
|
74553
74426
|
}
|
|
74554
|
-
// ===== Helper Methods =====
|
|
74555
|
-
hasNonZeroPixels(imageData) {
|
|
74556
|
-
const data = imageData.data;
|
|
74557
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
74558
|
-
if (data[i] !== 0 || data[i + 1] !== 0 || data[i + 2] !== 0 || data[i + 3] !== 0) {
|
|
74559
|
-
return true;
|
|
74560
|
-
}
|
|
74561
|
-
}
|
|
74562
|
-
return false;
|
|
74563
|
-
}
|
|
74564
74427
|
}
|
|
74565
74428
|
|
|
74566
74429
|
class DrawToolCore extends CommToolsData {
|
|
@@ -74639,8 +74502,7 @@ void main() {
|
|
|
74639
74502
|
this.sphereTool = new SphereTool(toolCtx, {
|
|
74640
74503
|
setEmptyCanvasSize: (axis) => this.setEmptyCanvasSize(axis),
|
|
74641
74504
|
drawImageOnEmptyImage: (canvas) => this.drawImageOnEmptyImage(canvas),
|
|
74642
|
-
storeImageToAxis: (index,
|
|
74643
|
-
createEmptyPaintImage: (dimensions, paintImages) => this.createEmptyPaintImage(dimensions, paintImages),
|
|
74505
|
+
storeImageToAxis: (index, imageData, axis) => this.imageStoreHelper.storeImageToAxis(index, imageData, axis),
|
|
74644
74506
|
});
|
|
74645
74507
|
this.crosshairTool = new CrosshairTool(toolCtx);
|
|
74646
74508
|
this.contrastTool = new ContrastTool(toolCtx, this.container, this.contrastEventPrameters, {
|
|
@@ -74663,40 +74525,28 @@ void main() {
|
|
|
74663
74525
|
// Use string comparison to avoid TypeScript narrowing issues
|
|
74664
74526
|
const prev = prevMode;
|
|
74665
74527
|
const next = newMode;
|
|
74666
|
-
// Sync EventRouter mode changes with existing state flags
|
|
74667
|
-
if (next === 'draw') {
|
|
74668
|
-
this.protectedData.Is_Shift_Pressed = true;
|
|
74669
|
-
this.nrrd_states.enableCursorChoose = false;
|
|
74670
|
-
}
|
|
74671
|
-
else if (prev === 'draw') {
|
|
74672
|
-
this.protectedData.Is_Shift_Pressed = false;
|
|
74673
|
-
}
|
|
74674
74528
|
if (next === 'contrast') {
|
|
74675
|
-
this.protectedData.Is_Ctrl_Pressed = true;
|
|
74676
|
-
this.protectedData.Is_Shift_Pressed = false;
|
|
74677
74529
|
this.configContrastDragMode();
|
|
74678
74530
|
}
|
|
74679
74531
|
else if (prev === 'contrast') {
|
|
74680
|
-
this.protectedData.Is_Ctrl_Pressed = false;
|
|
74681
74532
|
this.removeContrastDragMode();
|
|
74682
74533
|
this.gui_states.readyToUpdate = true;
|
|
74683
74534
|
}
|
|
74684
74535
|
if (next === 'crosshair') {
|
|
74685
|
-
this.nrrd_states.enableCursorChoose = true;
|
|
74686
74536
|
this.protectedData.Is_Draw = false;
|
|
74687
74537
|
}
|
|
74688
|
-
else if (prev === 'crosshair') {
|
|
74689
|
-
this.nrrd_states.enableCursorChoose = false;
|
|
74690
|
-
}
|
|
74691
74538
|
}
|
|
74692
74539
|
});
|
|
74540
|
+
// Inject eventRouter into ToolContext so tools can query mode/state
|
|
74541
|
+
const toolCtx = this.sphereTool['ctx'];
|
|
74542
|
+
toolCtx.eventRouter = this.eventRouter;
|
|
74693
74543
|
// Configure keyboard settings from class field
|
|
74694
74544
|
this.eventRouter.setKeyboardSettings(this._keyboardSettings);
|
|
74695
74545
|
// Track undo flag for Ctrl+Z handling
|
|
74696
74546
|
let undoFlag = false;
|
|
74697
74547
|
// Register keyboard handlers with EventRouter
|
|
74698
74548
|
this.eventRouter.setKeydownHandler((ev) => {
|
|
74699
|
-
var _a;
|
|
74549
|
+
var _a, _b;
|
|
74700
74550
|
if (this._configKeyBoard)
|
|
74701
74551
|
return;
|
|
74702
74552
|
// Handle undo (Ctrl+Z)
|
|
@@ -74717,15 +74567,16 @@ void main() {
|
|
|
74717
74567
|
}
|
|
74718
74568
|
}
|
|
74719
74569
|
// Handle draw mode (Shift key) - EventRouter already tracks this
|
|
74570
|
+
// EventRouter's handleKeyDown will enforce mutual exclusion
|
|
74720
74571
|
if (ev.key === this._keyboardSettings.draw && !this.gui_states.sphere && !this.gui_states.calculator) {
|
|
74721
|
-
if (this.
|
|
74572
|
+
if ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCtrlHeld()) {
|
|
74722
74573
|
return; // Ctrl takes priority
|
|
74723
74574
|
}
|
|
74724
74575
|
// EventRouter will set mode to 'draw' via internal handler
|
|
74725
74576
|
}
|
|
74726
74577
|
});
|
|
74727
74578
|
this.eventRouter.setKeyupHandler((ev) => {
|
|
74728
|
-
var _a, _b, _c, _d;
|
|
74579
|
+
var _a, _b, _c, _d, _e, _f;
|
|
74729
74580
|
if (this._configKeyBoard)
|
|
74730
74581
|
return;
|
|
74731
74582
|
// Handle Ctrl key release (contrast mode toggle)
|
|
@@ -74738,12 +74589,15 @@ void main() {
|
|
|
74738
74589
|
// Skip mode toggle when contrast shortcut is disabled
|
|
74739
74590
|
if (!((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.isContrastEnabled()))
|
|
74740
74591
|
return;
|
|
74592
|
+
// Block contrast toggle during crosshair or draw (mutual exclusion)
|
|
74593
|
+
if (((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled()) || ((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.getMode()) === 'draw')
|
|
74594
|
+
return;
|
|
74741
74595
|
// Toggle contrast mode manually since it's on keyup
|
|
74742
|
-
if (((
|
|
74743
|
-
(
|
|
74596
|
+
if (((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.getMode()) !== 'contrast') {
|
|
74597
|
+
(_e = this.eventRouter) === null || _e === void 0 ? void 0 : _e.setMode('contrast');
|
|
74744
74598
|
}
|
|
74745
74599
|
else {
|
|
74746
|
-
(
|
|
74600
|
+
(_f = this.eventRouter) === null || _f === void 0 ? void 0 : _f.setMode('idle');
|
|
74747
74601
|
}
|
|
74748
74602
|
}
|
|
74749
74603
|
});
|
|
@@ -74919,6 +74773,7 @@ void main() {
|
|
|
74919
74773
|
}
|
|
74920
74774
|
};
|
|
74921
74775
|
this.drawingPrameters.handleOnDrawingMouseDown = (e) => {
|
|
74776
|
+
var _a, _b, _c, _d;
|
|
74922
74777
|
if (leftclicked || rightclicked) {
|
|
74923
74778
|
this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
74924
74779
|
this.protectedData.ctxes.drawingLayerMasterCtx.closePath();
|
|
@@ -74934,7 +74789,7 @@ void main() {
|
|
|
74934
74789
|
// remove it when mouse click down
|
|
74935
74790
|
this.protectedData.canvases.drawingCanvas.removeEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
74936
74791
|
if (e.button === 0) {
|
|
74937
|
-
if (this.
|
|
74792
|
+
if (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode()) === 'draw') {
|
|
74938
74793
|
leftclicked = true;
|
|
74939
74794
|
lines = [];
|
|
74940
74795
|
Is_Painting = true;
|
|
@@ -74961,13 +74816,13 @@ void main() {
|
|
|
74961
74816
|
const vol = this.getVolumeForLayer(this.gui_states.layer);
|
|
74962
74817
|
this.preDrawSlice = vol.getSliceUint8(this.preDrawSliceIndex, this.preDrawAxis).data.slice();
|
|
74963
74818
|
}
|
|
74964
|
-
catch (
|
|
74819
|
+
catch (_e) {
|
|
74965
74820
|
this.preDrawSlice = null;
|
|
74966
74821
|
}
|
|
74967
74822
|
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
74968
74823
|
this.protectedData.canvases.drawingCanvas.addEventListener("pointermove", this.drawingPrameters.handleOnDrawingMouseMove);
|
|
74969
74824
|
}
|
|
74970
|
-
else if (this.
|
|
74825
|
+
else if ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled()) {
|
|
74971
74826
|
this.nrrd_states.cursorPageX =
|
|
74972
74827
|
e.offsetX / this.nrrd_states.sizeFoctor;
|
|
74973
74828
|
this.nrrd_states.cursorPageY =
|
|
@@ -74975,10 +74830,10 @@ void main() {
|
|
|
74975
74830
|
this.enableCrosshair();
|
|
74976
74831
|
this.protectedData.canvases.drawingCanvas.addEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
74977
74832
|
}
|
|
74978
|
-
else if (this.gui_states.sphere && !this.
|
|
74833
|
+
else if (this.gui_states.sphere && !((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.isCrosshairEnabled())) {
|
|
74979
74834
|
sphere(e);
|
|
74980
74835
|
}
|
|
74981
|
-
else if (this.gui_states.calculator && !this.
|
|
74836
|
+
else if (this.gui_states.calculator && !((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.isCrosshairEnabled())) {
|
|
74982
74837
|
this.drawCalSphereDown(e.offsetX, e.offsetY, this.nrrd_states.currentIndex, this.gui_states.cal_distance);
|
|
74983
74838
|
}
|
|
74984
74839
|
}
|
|
@@ -75043,8 +74898,9 @@ void main() {
|
|
|
75043
74898
|
ctx.drawImage(this.protectedData.canvases.emptyCanvas, 0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
75044
74899
|
};
|
|
75045
74900
|
this.drawingPrameters.handleOnDrawingMouseUp = (e) => {
|
|
74901
|
+
var _a, _b, _c, _d;
|
|
75046
74902
|
if (e.button === 0) {
|
|
75047
|
-
if (this.
|
|
74903
|
+
if (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode()) === 'draw' || Is_Painting) {
|
|
75048
74904
|
leftclicked = false;
|
|
75049
74905
|
let { ctx, canvas } = this.setCurrentLayer();
|
|
75050
74906
|
ctx.closePath();
|
|
@@ -75092,7 +74948,7 @@ void main() {
|
|
|
75092
74948
|
};
|
|
75093
74949
|
this.undoManager.push(delta);
|
|
75094
74950
|
}
|
|
75095
|
-
catch (
|
|
74951
|
+
catch (_e) {
|
|
75096
74952
|
// Volume not ready — skip
|
|
75097
74953
|
}
|
|
75098
74954
|
this.preDrawSlice = null;
|
|
@@ -75103,7 +74959,7 @@ void main() {
|
|
|
75103
74959
|
});
|
|
75104
74960
|
}
|
|
75105
74961
|
else if (this.gui_states.sphere &&
|
|
75106
|
-
!this.
|
|
74962
|
+
!((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled())) {
|
|
75107
74963
|
// plan B
|
|
75108
74964
|
// findout all index in the sphere radius range in Axial view
|
|
75109
74965
|
if (this.nrrd_states.spherePlanB) {
|
|
@@ -75123,12 +74979,12 @@ void main() {
|
|
|
75123
74979
|
this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
75124
74980
|
}
|
|
75125
74981
|
else if ((this.gui_states.sphere || this.gui_states.calculator) &&
|
|
75126
|
-
this.
|
|
74982
|
+
((_c = this.eventRouter) === null || _c === void 0 ? void 0 : _c.isCrosshairEnabled())) {
|
|
75127
74983
|
this.protectedData.canvases.drawingCanvas.addEventListener("wheel", this.drawingPrameters.handleMouseZoomSliceWheel);
|
|
75128
74984
|
this.protectedData.canvases.drawingCanvas.removeEventListener("pointerup", this.drawingPrameters.handleOnDrawingMouseUp);
|
|
75129
74985
|
}
|
|
75130
74986
|
else if (this.gui_states.calculator &&
|
|
75131
|
-
!this.
|
|
74987
|
+
!((_d = this.eventRouter) === null || _d === void 0 ? void 0 : _d.isCrosshairEnabled())) {
|
|
75132
74988
|
// When mouse up
|
|
75133
74989
|
this.drawCalSphereUp();
|
|
75134
74990
|
}
|
|
@@ -75170,6 +75026,7 @@ void main() {
|
|
|
75170
75026
|
}
|
|
75171
75027
|
});
|
|
75172
75028
|
this.start = () => {
|
|
75029
|
+
var _a, _b;
|
|
75173
75030
|
if (this.gui_states.readyToUpdate) {
|
|
75174
75031
|
this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
75175
75032
|
this.protectedData.ctxes.drawingCtx.globalAlpha =
|
|
@@ -75183,7 +75040,10 @@ void main() {
|
|
|
75183
75040
|
}
|
|
75184
75041
|
}
|
|
75185
75042
|
else {
|
|
75186
|
-
|
|
75043
|
+
// Use EventRouter mode for mutually exclusive crosshair vs draw rendering
|
|
75044
|
+
const currentMode = (_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.getMode();
|
|
75045
|
+
if (currentMode === 'draw') {
|
|
75046
|
+
// Draw mode: show brush circle preview
|
|
75187
75047
|
if (!this.gui_states.pencil &&
|
|
75188
75048
|
!this.gui_states.Eraser &&
|
|
75189
75049
|
this.nrrd_states.Mouse_Over) {
|
|
@@ -75197,7 +75057,8 @@ void main() {
|
|
|
75197
75057
|
this.protectedData.ctxes.drawingCtx.stroke();
|
|
75198
75058
|
}
|
|
75199
75059
|
}
|
|
75200
|
-
if (this.
|
|
75060
|
+
else if (currentMode === 'crosshair' || ((_b = this.eventRouter) === null || _b === void 0 ? void 0 : _b.isCrosshairEnabled())) {
|
|
75061
|
+
// Crosshair mode: show red cross lines (mutually exclusive with draw)
|
|
75201
75062
|
this.protectedData.ctxes.drawingCtx.clearRect(0, 0, this.nrrd_states.changedWidth, this.nrrd_states.changedHeight);
|
|
75202
75063
|
const ex = this.nrrd_states.cursorPageX * this.nrrd_states.sizeFoctor;
|
|
75203
75064
|
const ey = this.nrrd_states.cursorPageY * this.nrrd_states.sizeFoctor;
|
|
@@ -75353,19 +75214,18 @@ void main() {
|
|
|
75353
75214
|
/**
|
|
75354
75215
|
* Clear mask on current slice canvas.
|
|
75355
75216
|
*
|
|
75356
|
-
*
|
|
75357
|
-
*
|
|
75358
|
-
*
|
|
75217
|
+
* Only clears the active layer's MaskVolume slice data.
|
|
75218
|
+
* Other layers are left untouched. After clearing, all layer canvases
|
|
75219
|
+
* are re-rendered from MaskVolume to keep visuals in sync.
|
|
75359
75220
|
*/
|
|
75360
75221
|
clearPaint() {
|
|
75361
75222
|
this.protectedData.Is_Draw = true;
|
|
75362
|
-
this.resetLayerCanvas();
|
|
75363
75223
|
this.protectedData.canvases.originCanvas.width =
|
|
75364
75224
|
this.protectedData.canvases.originCanvas.width;
|
|
75365
75225
|
this.protectedData.mainPreSlices.repaint.call(this.protectedData.mainPreSlices);
|
|
75366
75226
|
this.protectedData.previousDrawingImage =
|
|
75367
75227
|
this.protectedData.ctxes.emptyCtx.createImageData(1, 1);
|
|
75368
|
-
//
|
|
75228
|
+
// Clear only the active layer's MaskVolume slice and record undo delta
|
|
75369
75229
|
try {
|
|
75370
75230
|
const axis = this.protectedData.axis;
|
|
75371
75231
|
const idx = this.nrrd_states.currentIndex;
|
|
@@ -75373,11 +75233,8 @@ void main() {
|
|
|
75373
75233
|
const vol = this.getVolumeForLayer(activeLayer);
|
|
75374
75234
|
// Capture old slice for undo before clearing
|
|
75375
75235
|
const oldSlice = vol.getSliceUint8(idx, axis).data.slice();
|
|
75376
|
-
// Clear only the active layer
|
|
75377
|
-
|
|
75378
|
-
layer1.clearSlice(idx, axis);
|
|
75379
|
-
layer2.clearSlice(idx, axis);
|
|
75380
|
-
layer3.clearSlice(idx, axis);
|
|
75236
|
+
// Clear only the active layer's MaskVolume slice
|
|
75237
|
+
vol.clearSlice(idx, axis);
|
|
75381
75238
|
// New (all-zero) slice for undo newSlice
|
|
75382
75239
|
const { data: newSlice, width, height } = vol.getSliceUint8(idx, axis);
|
|
75383
75240
|
// Push clearPaint delta to UndoManager (supports undo)
|
|
@@ -75396,12 +75253,23 @@ void main() {
|
|
|
75396
75253
|
}
|
|
75397
75254
|
}
|
|
75398
75255
|
catch (_a) {
|
|
75399
|
-
// Volume not ready (1×1×1 placeholder)
|
|
75256
|
+
// Volume not ready (1×1×1 placeholder)
|
|
75257
|
+
}
|
|
75258
|
+
// Re-render ALL layers from MaskVolume to canvas (rebuilds visuals from source of truth)
|
|
75259
|
+
this.resetLayerCanvas();
|
|
75260
|
+
const buffer = this.getOrCreateSliceBuffer(this.protectedData.axis);
|
|
75261
|
+
if (buffer) {
|
|
75262
|
+
const w = this.nrrd_states.changedWidth;
|
|
75263
|
+
const h = this.nrrd_states.changedHeight;
|
|
75264
|
+
for (const layerId of this.nrrd_states.layers) {
|
|
75265
|
+
const target = this.protectedData.layerTargets.get(layerId);
|
|
75266
|
+
if (!target)
|
|
75267
|
+
continue;
|
|
75268
|
+
target.ctx.clearRect(0, 0, w, h);
|
|
75269
|
+
this.renderSliceToCanvas(layerId, this.protectedData.axis, this.nrrd_states.currentIndex, buffer, target.ctx, w, h);
|
|
75270
|
+
}
|
|
75400
75271
|
}
|
|
75401
|
-
this.
|
|
75402
|
-
const restLayers = this.getRestLayer();
|
|
75403
|
-
this.storeEachLayerImage(this.nrrd_states.currentIndex, restLayers[0]);
|
|
75404
|
-
this.storeEachLayerImage(this.nrrd_states.currentIndex, restLayers[1]);
|
|
75272
|
+
this.compositeAllLayers();
|
|
75405
75273
|
this.setIsDrawFalse(1000);
|
|
75406
75274
|
}
|
|
75407
75275
|
/**
|
|
@@ -75482,14 +75350,11 @@ void main() {
|
|
|
75482
75350
|
this.protectedData.ctxes.drawingLayerMasterCtx.getImageData(0, 0, this.protectedData.canvases.drawingCanvasLayerMaster.width, this.protectedData.canvases.drawingCanvasLayerMaster.height);
|
|
75483
75351
|
}
|
|
75484
75352
|
/****************************Store images (delegated to ImageStoreHelper)****************************************************/
|
|
75485
|
-
storeImageToAxis(index, paintedImages, imageData, axis) {
|
|
75486
|
-
this.imageStoreHelper.storeImageToAxis(index, paintedImages, imageData, axis);
|
|
75487
|
-
}
|
|
75488
75353
|
storeAllImages(index, layer) {
|
|
75489
75354
|
this.imageStoreHelper.storeAllImages(index, layer);
|
|
75490
75355
|
}
|
|
75491
|
-
storeImageToLayer(index, canvas
|
|
75492
|
-
return this.imageStoreHelper.storeImageToLayer(index, canvas
|
|
75356
|
+
storeImageToLayer(index, canvas) {
|
|
75357
|
+
return this.imageStoreHelper.storeImageToLayer(index, canvas);
|
|
75493
75358
|
}
|
|
75494
75359
|
storeEachLayerImage(index, layer) {
|
|
75495
75360
|
this.imageStoreHelper.storeEachLayerImage(index, layer);
|
|
@@ -76185,8 +76050,9 @@ void main() {
|
|
|
76185
76050
|
* @param {string} aixs:"x" | "y" | "z"
|
|
76186
76051
|
* */
|
|
76187
76052
|
setSliceOrientation(axisTo) {
|
|
76053
|
+
var _a;
|
|
76188
76054
|
let convetObj;
|
|
76189
|
-
if (this.
|
|
76055
|
+
if (((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.isCrosshairEnabled()) || this.gui_states.sphere) {
|
|
76190
76056
|
if (this.protectedData.axis === "z") {
|
|
76191
76057
|
this.cursorPage.z.index = this.nrrd_states.currentIndex;
|
|
76192
76058
|
this.cursorPage.z.cursorPageX = this.nrrd_states.cursorPageX;
|
|
@@ -76571,6 +76437,8 @@ void main() {
|
|
|
76571
76437
|
}
|
|
76572
76438
|
// Invalidate reusable slice buffer
|
|
76573
76439
|
this.invalidateSliceBuffer();
|
|
76440
|
+
// Reload all layers to canvas (restores other layers' visuals)
|
|
76441
|
+
this.reloadMasksFromVolume();
|
|
76574
76442
|
}
|
|
76575
76443
|
/**
|
|
76576
76444
|
* Reset the draw and display canvases layout after mouse pan.
|
|
@@ -76781,7 +76649,8 @@ void main() {
|
|
|
76781
76649
|
*/
|
|
76782
76650
|
configMouseSliceWheel() {
|
|
76783
76651
|
const handleMouseZoomSliceWheelMove = (e) => {
|
|
76784
|
-
|
|
76652
|
+
var _a;
|
|
76653
|
+
if ((_a = this.eventRouter) === null || _a === void 0 ? void 0 : _a.isShiftHeld()) {
|
|
76785
76654
|
return;
|
|
76786
76655
|
}
|
|
76787
76656
|
e.preventDefault();
|
|
@@ -77021,7 +76890,7 @@ void main() {
|
|
|
77021
76890
|
}
|
|
77022
76891
|
|
|
77023
76892
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
77024
|
-
const REVISION = "v3.0.
|
|
76893
|
+
const REVISION = "v3.0.1-beta";
|
|
77025
76894
|
console.log(`%cCopper3D Visualisation %cBeta:${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
77026
76895
|
|
|
77027
76896
|
exports.CHANNEL_COLORS = CHANNEL_COLORS;
|