copper3d 3.6.5 → 3.6.7
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/NrrdTools.d.ts +18 -4
- package/dist/Utils/segmentation/NrrdTools.js +39 -7
- package/dist/Utils/segmentation/NrrdTools.js.map +1 -1
- package/dist/Utils/surfaceAnnotation/MeshGraph.d.ts +4 -0
- package/dist/Utils/surfaceAnnotation/MeshGraph.js +9 -0
- package/dist/Utils/surfaceAnnotation/MeshGraph.js.map +1 -1
- package/dist/Utils/surfaceAnnotation/SurfaceAnnotator.d.ts +31 -0
- package/dist/Utils/surfaceAnnotation/SurfaceAnnotator.js +138 -12
- package/dist/Utils/surfaceAnnotation/SurfaceAnnotator.js.map +1 -1
- package/dist/Utils/surfaceAnnotation/annotationStore.d.ts +2 -0
- package/dist/Utils/surfaceAnnotation/annotationStore.js +8 -0
- package/dist/Utils/surfaceAnnotation/annotationStore.js.map +1 -1
- package/dist/Utils/surfaceAnnotation/types.d.ts +1 -0
- package/dist/Utils/surfaceAnnotation/types.js.map +1 -1
- package/dist/bundle.esm.js +206 -22
- package/dist/bundle.umd.js +206 -22
- package/dist/index.d.ts +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/types/Utils/segmentation/NrrdTools.d.ts +18 -4
- package/dist/types/Utils/surfaceAnnotation/MeshGraph.d.ts +4 -0
- package/dist/types/Utils/surfaceAnnotation/SurfaceAnnotator.d.ts +31 -0
- package/dist/types/Utils/surfaceAnnotation/annotationStore.d.ts +2 -0
- package/dist/types/Utils/surfaceAnnotation/types.d.ts +1 -0
- package/package.json +1 -1
package/dist/bundle.umd.js
CHANGED
|
@@ -61173,6 +61173,15 @@ void main() {
|
|
|
61173
61173
|
const nm = new Matrix3().getNormalMatrix(mesh.matrixWorld);
|
|
61174
61174
|
return n.applyMatrix3(nm).normalize();
|
|
61175
61175
|
}
|
|
61176
|
+
/** Vertex local normal at index i (reads from the welded graphGeo normal attribute). */
|
|
61177
|
+
vertexNormalLocal(i) {
|
|
61178
|
+
return new Vector3(this.normals ? this.normals[i * 3] : 0, this.normals ? this.normals[i * 3 + 1] : 0, this.normals ? this.normals[i * 3 + 2] : 1);
|
|
61179
|
+
}
|
|
61180
|
+
/** Normal at the vertex nearest to localPoint (for recovering normals from imported point coordinates). */
|
|
61181
|
+
nearestNormalLocal(p) {
|
|
61182
|
+
const i = this.nearestVertex(p);
|
|
61183
|
+
return this.vertexNormalLocal(i);
|
|
61184
|
+
}
|
|
61176
61185
|
/** Vertex local coordinates + local normal (the graph geometry is already local space). */
|
|
61177
61186
|
vertexLocal(i) {
|
|
61178
61187
|
return {
|
|
@@ -61398,6 +61407,13 @@ void main() {
|
|
|
61398
61407
|
this.notify();
|
|
61399
61408
|
}
|
|
61400
61409
|
}
|
|
61410
|
+
setVisible(id, visible) {
|
|
61411
|
+
const a = this.get(id);
|
|
61412
|
+
if (a) {
|
|
61413
|
+
a.visible = visible;
|
|
61414
|
+
this.notify();
|
|
61415
|
+
}
|
|
61416
|
+
}
|
|
61401
61417
|
toJSON(modelName, mesh, opts = {}) {
|
|
61402
61418
|
var _a;
|
|
61403
61419
|
const space = (_a = opts.space) !== null && _a !== void 0 ? _a : "local";
|
|
@@ -61422,6 +61438,7 @@ void main() {
|
|
|
61422
61438
|
label: a.label,
|
|
61423
61439
|
color: a.color,
|
|
61424
61440
|
closed: a.closed,
|
|
61441
|
+
visible: a.visible,
|
|
61425
61442
|
points: a.vertices.map(toPt),
|
|
61426
61443
|
})),
|
|
61427
61444
|
};
|
|
@@ -61441,6 +61458,10 @@ void main() {
|
|
|
61441
61458
|
var _a, _b, _c;
|
|
61442
61459
|
this.mode = "navigate";
|
|
61443
61460
|
this.spaceHeld = false;
|
|
61461
|
+
this.drawLock = false;
|
|
61462
|
+
this.armed = "freehand";
|
|
61463
|
+
this.spaceDownAt = 0;
|
|
61464
|
+
this.spaceDragged = false;
|
|
61444
61465
|
this.pointerDown = false;
|
|
61445
61466
|
this.store = new AnnotationStore();
|
|
61446
61467
|
this.managed = new Set();
|
|
@@ -61467,14 +61488,18 @@ void main() {
|
|
|
61467
61488
|
(this.activeGeoLine.material.resolution.set(w, h));
|
|
61468
61489
|
};
|
|
61469
61490
|
this.onPointerDown = (e) => {
|
|
61470
|
-
if (this.spaceHeld)
|
|
61471
|
-
return;
|
|
61472
61491
|
if (e.button !== 0)
|
|
61473
61492
|
return; // left button only
|
|
61474
61493
|
if (!this.insideContainer(e))
|
|
61475
61494
|
return;
|
|
61476
61495
|
this.pointerDown = true;
|
|
61477
|
-
|
|
61496
|
+
// Only act with the armed tool when in drawing mode (drawLock or spaceHeld).
|
|
61497
|
+
// When not drawing, navigation is the default — pointer events go to camera controls.
|
|
61498
|
+
if (!this.drawing)
|
|
61499
|
+
return;
|
|
61500
|
+
// Use armed tool (not this.mode which may be "navigate")
|
|
61501
|
+
const activeTool = this.armed;
|
|
61502
|
+
if (activeTool === "point") {
|
|
61478
61503
|
const h = this.hit(e);
|
|
61479
61504
|
if (!h)
|
|
61480
61505
|
return;
|
|
@@ -61488,13 +61513,14 @@ void main() {
|
|
|
61488
61513
|
label: `Point ${this.seq}`,
|
|
61489
61514
|
color: this.pointColorVal,
|
|
61490
61515
|
closed: false,
|
|
61516
|
+
visible: true,
|
|
61491
61517
|
vertices: [v],
|
|
61492
61518
|
object3D: marker,
|
|
61493
61519
|
};
|
|
61494
61520
|
this.store.add(ann);
|
|
61495
61521
|
return;
|
|
61496
61522
|
}
|
|
61497
|
-
if (
|
|
61523
|
+
if (activeTool === "freehand") {
|
|
61498
61524
|
this.activeStroke = new StrokeContour(this.minGap, this.maxJump, this.o.mesh);
|
|
61499
61525
|
this.activeStroke.begin();
|
|
61500
61526
|
const h = this.hit(e);
|
|
@@ -61504,7 +61530,7 @@ void main() {
|
|
|
61504
61530
|
this.o.scene.add(this.activeLine);
|
|
61505
61531
|
return;
|
|
61506
61532
|
}
|
|
61507
|
-
if (
|
|
61533
|
+
if (activeTool === "geodesic") {
|
|
61508
61534
|
// First check whether an existing anchor was clicked → cancel that point (any point can be canceled).
|
|
61509
61535
|
const pick = this.pickGeoMarker(e);
|
|
61510
61536
|
if (pick >= 0 && this.activeGeo) {
|
|
@@ -61531,15 +61557,26 @@ void main() {
|
|
|
61531
61557
|
}
|
|
61532
61558
|
};
|
|
61533
61559
|
this.onPointerMove = (e) => {
|
|
61534
|
-
if (
|
|
61535
|
-
|
|
61560
|
+
// Track if user dragged while space was held (so a hold-drag is not treated as a tap).
|
|
61561
|
+
if (this.spaceHeld && this.pointerDown) {
|
|
61562
|
+
this.spaceDragged = true;
|
|
61563
|
+
}
|
|
61564
|
+
// When not drawing, navigation is default — don't interfere with camera controls.
|
|
61565
|
+
if (!this.drawing) {
|
|
61566
|
+
// In geodesic mode we can still show the hover hint when not drawing.
|
|
61567
|
+
if (this.armed === "geodesic" && !this.pointerDown) {
|
|
61568
|
+
this.setGeoHover(this.insideContainer(e) ? this.pickGeoMarker(e) : -1);
|
|
61569
|
+
}
|
|
61570
|
+
else {
|
|
61571
|
+
this.setGeoHover(-1);
|
|
61572
|
+
}
|
|
61536
61573
|
return;
|
|
61537
61574
|
}
|
|
61538
|
-
//
|
|
61539
|
-
if (this.
|
|
61575
|
+
// Drawing mode: geodesic hover hint
|
|
61576
|
+
if (this.armed === "geodesic" && !this.pointerDown) {
|
|
61540
61577
|
this.setGeoHover(this.insideContainer(e) ? this.pickGeoMarker(e) : -1);
|
|
61541
61578
|
}
|
|
61542
|
-
if (this.
|
|
61579
|
+
if (this.armed === "freehand" &&
|
|
61543
61580
|
this.pointerDown &&
|
|
61544
61581
|
this.activeStroke &&
|
|
61545
61582
|
this.activeLine) {
|
|
@@ -61564,6 +61601,7 @@ void main() {
|
|
|
61564
61601
|
label: `Contour ${this.seq}`,
|
|
61565
61602
|
color: this.freehandColor,
|
|
61566
61603
|
closed: false,
|
|
61604
|
+
visible: true,
|
|
61567
61605
|
vertices: verts,
|
|
61568
61606
|
object3D: this.activeLine,
|
|
61569
61607
|
};
|
|
@@ -61581,11 +61619,17 @@ void main() {
|
|
|
61581
61619
|
if (this.isTypingTarget(e))
|
|
61582
61620
|
return;
|
|
61583
61621
|
if (e.code === "Space") {
|
|
61622
|
+
if (!this.spaceHeld) {
|
|
61623
|
+
this.spaceDownAt = performance.now();
|
|
61624
|
+
this.spaceDragged = false;
|
|
61625
|
+
}
|
|
61584
61626
|
this.spaceHeld = true;
|
|
61585
61627
|
this.applyCameraGating();
|
|
61628
|
+
e.preventDefault();
|
|
61586
61629
|
return;
|
|
61587
61630
|
}
|
|
61588
61631
|
if (e.key === "Escape") {
|
|
61632
|
+
this.drawLock = false;
|
|
61589
61633
|
this.setMode("navigate");
|
|
61590
61634
|
return;
|
|
61591
61635
|
}
|
|
@@ -61618,7 +61662,11 @@ void main() {
|
|
|
61618
61662
|
};
|
|
61619
61663
|
this.onKeyUp = (e) => {
|
|
61620
61664
|
if (e.code === "Space") {
|
|
61665
|
+
const held = performance.now() - this.spaceDownAt;
|
|
61621
61666
|
this.spaceHeld = false;
|
|
61667
|
+
if (held <= SurfaceAnnotator.TAP_MS && !this.spaceDragged) {
|
|
61668
|
+
this.drawLock = !this.drawLock; // tap toggles lock
|
|
61669
|
+
}
|
|
61622
61670
|
this.applyCameraGating();
|
|
61623
61671
|
}
|
|
61624
61672
|
};
|
|
@@ -61674,6 +61722,10 @@ void main() {
|
|
|
61674
61722
|
if (m !== "geodesic" && this.activeGeo)
|
|
61675
61723
|
this.clearActiveGeo();
|
|
61676
61724
|
this.mode = m;
|
|
61725
|
+
// Record armed tool when choosing a drawing mode (not navigate).
|
|
61726
|
+
// Camera gating is NOT changed here — the user must use Space to enter drawing.
|
|
61727
|
+
if (m !== "navigate")
|
|
61728
|
+
this.armed = m;
|
|
61677
61729
|
this.applyCameraGating();
|
|
61678
61730
|
(_b = (_a = this.o).onModeChange) === null || _b === void 0 ? void 0 : _b.call(_a, m);
|
|
61679
61731
|
}
|
|
@@ -61736,10 +61788,92 @@ void main() {
|
|
|
61736
61788
|
exportJSON(modelName, opts) {
|
|
61737
61789
|
return this.store.toJSON(modelName, this.o.mesh, opts);
|
|
61738
61790
|
}
|
|
61791
|
+
setVisible(id, visible) {
|
|
61792
|
+
this.store.setVisible(id, visible);
|
|
61793
|
+
}
|
|
61794
|
+
/**
|
|
61795
|
+
* Rebuild annotations from an exported payload (local-space points). Normals are taken from the
|
|
61796
|
+
* point when present ([x,y,z,nx,ny,nz]); otherwise recovered from the welded graph's nearest
|
|
61797
|
+
* vertex. Each imported item becomes first-class (select/recolor/hide/delete/export).
|
|
61798
|
+
*/
|
|
61799
|
+
importAnnotations(payload) {
|
|
61800
|
+
var _a, _b, _c, _d;
|
|
61801
|
+
let count = 0;
|
|
61802
|
+
let maxImported = this.seq;
|
|
61803
|
+
for (const a of (_a = payload.annotations) !== null && _a !== void 0 ? _a : []) {
|
|
61804
|
+
const verts = a.points.map((p) => {
|
|
61805
|
+
const [x, y, z] = p;
|
|
61806
|
+
let nx = p[3], ny = p[4], nz = p[5];
|
|
61807
|
+
if (nx === undefined || ny === undefined || nz === undefined) {
|
|
61808
|
+
const nrm = this.graph.nearestNormalLocal(new Vector3(x, y, z));
|
|
61809
|
+
nx = nrm.x;
|
|
61810
|
+
ny = nrm.y;
|
|
61811
|
+
nz = nrm.z;
|
|
61812
|
+
}
|
|
61813
|
+
return { x, y, z, nx, ny, nz, faceIndex: 0 };
|
|
61814
|
+
});
|
|
61815
|
+
if (a.type === "points") {
|
|
61816
|
+
for (const v of verts) {
|
|
61817
|
+
// Only reuse the provided id for a single-point entry (the round-trip case);
|
|
61818
|
+
// a multi-point points entry must get a fresh id per marker to avoid duplicate keys.
|
|
61819
|
+
const id = a.id && verts.length === 1 ? a.id : this.nextId();
|
|
61820
|
+
const marker = makePointMarker(v, this.o.mesh, a.color, this.markerRadius);
|
|
61821
|
+
this.o.scene.add(marker);
|
|
61822
|
+
this.store.add({
|
|
61823
|
+
id,
|
|
61824
|
+
type: "points",
|
|
61825
|
+
mode: null,
|
|
61826
|
+
label: a.label,
|
|
61827
|
+
color: a.color,
|
|
61828
|
+
closed: false,
|
|
61829
|
+
visible: (_b = a.visible) !== null && _b !== void 0 ? _b : true,
|
|
61830
|
+
vertices: [v],
|
|
61831
|
+
object3D: marker,
|
|
61832
|
+
});
|
|
61833
|
+
// track max numeric id
|
|
61834
|
+
const m = id.match(/^a(\d+)$/);
|
|
61835
|
+
if (m)
|
|
61836
|
+
maxImported = Math.max(maxImported, parseInt(m[1], 10));
|
|
61837
|
+
count++;
|
|
61838
|
+
}
|
|
61839
|
+
}
|
|
61840
|
+
else {
|
|
61841
|
+
if (verts.length < 2)
|
|
61842
|
+
continue;
|
|
61843
|
+
const id = (_c = a.id) !== null && _c !== void 0 ? _c : this.nextId();
|
|
61844
|
+
const line = makeContourLine(verts, a.color, a.closed, this.o.container, this.epsilon, this.o.mesh);
|
|
61845
|
+
this.o.scene.add(line);
|
|
61846
|
+
this.store.add({
|
|
61847
|
+
id,
|
|
61848
|
+
type: "contour",
|
|
61849
|
+
mode: a.mode,
|
|
61850
|
+
label: a.label,
|
|
61851
|
+
color: a.color,
|
|
61852
|
+
closed: a.closed,
|
|
61853
|
+
visible: (_d = a.visible) !== null && _d !== void 0 ? _d : true,
|
|
61854
|
+
vertices: verts,
|
|
61855
|
+
object3D: line,
|
|
61856
|
+
});
|
|
61857
|
+
const m = id.match(/^a(\d+)$/);
|
|
61858
|
+
if (m)
|
|
61859
|
+
maxImported = Math.max(maxImported, parseInt(m[1], 10));
|
|
61860
|
+
count++;
|
|
61861
|
+
}
|
|
61862
|
+
}
|
|
61863
|
+
// keep seq ahead of any imported numeric ids to avoid collisions
|
|
61864
|
+
if (maxImported > this.seq)
|
|
61865
|
+
this.seq = maxImported;
|
|
61866
|
+
return count;
|
|
61867
|
+
}
|
|
61739
61868
|
// ---- Internal ----
|
|
61869
|
+
get drawing() {
|
|
61870
|
+
return this.drawLock || this.spaceHeld;
|
|
61871
|
+
}
|
|
61740
61872
|
applyCameraGating() {
|
|
61741
|
-
|
|
61742
|
-
|
|
61873
|
+
var _a, _b;
|
|
61874
|
+
// Default is navigate (camera enabled). Drawing only when drawLock or spaceHeld.
|
|
61875
|
+
this.o.controls.enabled = !this.drawing;
|
|
61876
|
+
(_b = (_a = this.o).onInteractionChange) === null || _b === void 0 ? void 0 : _b.call(_a, { drawing: this.drawing, armed: this.armed, locked: this.drawLock });
|
|
61743
61877
|
}
|
|
61744
61878
|
/** Reconcile the scene against store.list(): add missing objects, remove deleted ones (no dispose, kept for undo restore). */
|
|
61745
61879
|
reconcile() {
|
|
@@ -61757,6 +61891,11 @@ void main() {
|
|
|
61757
61891
|
this.o.scene.add(o);
|
|
61758
61892
|
}
|
|
61759
61893
|
this.managed = present;
|
|
61894
|
+
// Apply per-annotation visibility
|
|
61895
|
+
for (const a of this.store.list()) {
|
|
61896
|
+
if (a.object3D)
|
|
61897
|
+
a.object3D.visible = a.visible;
|
|
61898
|
+
}
|
|
61760
61899
|
this.applySelection();
|
|
61761
61900
|
(_b = (_a = this.o).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, this.store.list());
|
|
61762
61901
|
}
|
|
@@ -61764,6 +61903,8 @@ void main() {
|
|
|
61764
61903
|
for (const a of this.store.list()) {
|
|
61765
61904
|
if (!a.object3D)
|
|
61766
61905
|
continue;
|
|
61906
|
+
if (!a.visible)
|
|
61907
|
+
continue;
|
|
61767
61908
|
const sel = a.id === this.selectedId;
|
|
61768
61909
|
if (a.type === "contour") {
|
|
61769
61910
|
const mat = a.object3D.material;
|
|
@@ -61996,6 +62137,7 @@ void main() {
|
|
|
61996
62137
|
label: `Contour ${this.seq}`,
|
|
61997
62138
|
color: this.geodesicColor,
|
|
61998
62139
|
closed,
|
|
62140
|
+
visible: true,
|
|
61999
62141
|
vertices: verts,
|
|
62000
62142
|
object3D: this.activeGeoLine,
|
|
62001
62143
|
};
|
|
@@ -62029,7 +62171,8 @@ void main() {
|
|
|
62029
62171
|
window.removeEventListener("keyup", this.onKeyUp);
|
|
62030
62172
|
window.removeEventListener("resize", this.onResize);
|
|
62031
62173
|
}
|
|
62032
|
-
}
|
|
62174
|
+
}
|
|
62175
|
+
SurfaceAnnotator.TAP_MS = 250;
|
|
62033
62176
|
|
|
62034
62177
|
class copperScene extends baseScene {
|
|
62035
62178
|
// rayster pick
|
|
@@ -83019,12 +83162,20 @@ void main() {
|
|
|
83019
83162
|
return this.drawCore.aiAssistTool.getScratchSegments();
|
|
83020
83163
|
}
|
|
83021
83164
|
/**
|
|
83022
|
-
* Merge the AI scratch into a target layer
|
|
83023
|
-
* merge —
|
|
83024
|
-
*
|
|
83025
|
-
*
|
|
83165
|
+
* Merge the AI scratch into a target layer with an explicit per-segmentation
|
|
83166
|
+
* mapping, as a single undoable group (sandbox merge — non-destructive + one
|
|
83167
|
+
* Ctrl+Z). `mapping` is `{ AI scratch label -> target channel(1-8) }`; any label
|
|
83168
|
+
* absent from the mapping (or mapped to 0) is DISCARDED (not merged). Several AI
|
|
83169
|
+
* labels MAY map to the SAME channel — after merge the channel is the identity
|
|
83170
|
+
* (CLAUDE.md decision M2), so the merged voxels take that channel's fixed colour.
|
|
83171
|
+
*
|
|
83172
|
+
* UNION semantics (M4): an AI voxel only fills a target voxel that is currently
|
|
83173
|
+
* EMPTY (0) — existing manual annotation is NEVER erased. Where two AI labels map
|
|
83174
|
+
* to different channels and overlap the same voxel, the first one written wins
|
|
83175
|
+
* (iteration order), an accepted edge case for a single-value-per-voxel mask.
|
|
83176
|
+
* Scans all z-slices, so voxels painted from any view are caught.
|
|
83026
83177
|
*/
|
|
83027
|
-
|
|
83178
|
+
aiCommitToLayerMapped(targetLayer, mapping) {
|
|
83028
83179
|
const scratch = this.drawCore.aiAssistTool.getScratchVolume();
|
|
83029
83180
|
if (!scratch)
|
|
83030
83181
|
return;
|
|
@@ -83046,10 +83197,21 @@ void main() {
|
|
|
83046
83197
|
continue;
|
|
83047
83198
|
const oldSlice = target.getSliceUint8(z, "z").data; // copy
|
|
83048
83199
|
const newSlice = oldSlice.slice();
|
|
83200
|
+
let changed = false;
|
|
83049
83201
|
for (let i = 0; i < newSlice.length; i++) {
|
|
83050
|
-
|
|
83051
|
-
|
|
83202
|
+
const label = sc[i];
|
|
83203
|
+
if (label === 0)
|
|
83204
|
+
continue;
|
|
83205
|
+
const ch = mapping[label];
|
|
83206
|
+
if (!ch)
|
|
83207
|
+
continue; // unmapped / discarded segmentation
|
|
83208
|
+
if (newSlice[i] === 0) { // union: fill only EMPTY voxels (M4)
|
|
83209
|
+
newSlice[i] = ch;
|
|
83210
|
+
changed = true;
|
|
83211
|
+
}
|
|
83052
83212
|
}
|
|
83213
|
+
if (!changed)
|
|
83214
|
+
continue; // nothing landed on this slice → no delta
|
|
83053
83215
|
target.setSliceUint8(z, newSlice, "z");
|
|
83054
83216
|
deltas.push({ layerId: targetLayer, axis: "z", sliceIndex: z, oldSlice, newSlice: newSlice.slice() });
|
|
83055
83217
|
}
|
|
@@ -83057,6 +83219,19 @@ void main() {
|
|
|
83057
83219
|
this.drawCore.undoManager.pushGroup(deltas);
|
|
83058
83220
|
this.reloadMasksFromVolume();
|
|
83059
83221
|
}
|
|
83222
|
+
/**
|
|
83223
|
+
* Back-compat shim: merge with an IDENTITY mapping (each AI label → the same
|
|
83224
|
+
* channel number) via the mapped path above. Newer callers should use
|
|
83225
|
+
* `aiCommitToLayerMapped` with an explicit per-segmentation mapping.
|
|
83226
|
+
*/
|
|
83227
|
+
aiCommitToLayer(targetLayer = "layer1") {
|
|
83228
|
+
var _a;
|
|
83229
|
+
const segs = this.drawCore.aiAssistTool.getScratchSegments();
|
|
83230
|
+
const mapping = {};
|
|
83231
|
+
for (const s of (_a = segs === null || segs === void 0 ? void 0 : segs.segments) !== null && _a !== void 0 ? _a : [])
|
|
83232
|
+
mapping[s.label] = s.label;
|
|
83233
|
+
this.aiCommitToLayerMapped(targetLayer, mapping);
|
|
83234
|
+
}
|
|
83060
83235
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
83061
83236
|
// 11. Clear / Reset
|
|
83062
83237
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -83420,8 +83595,17 @@ void main() {
|
|
|
83420
83595
|
}
|
|
83421
83596
|
|
|
83422
83597
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
83423
|
-
// "v3.6.
|
|
83424
|
-
|
|
83598
|
+
// "v3.6.7" is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version.
|
|
83599
|
+
// When copper3d is consumed from local source (no rollup replace step), the identifier is undefined and
|
|
83600
|
+
// referencing it throws a ReferenceError — guard it so local loading still works.
|
|
83601
|
+
let _revision = "unknown";
|
|
83602
|
+
try {
|
|
83603
|
+
_revision = "v3.6.7";
|
|
83604
|
+
}
|
|
83605
|
+
catch (_a) {
|
|
83606
|
+
/* "v3.6.7" not injected (local source build) */
|
|
83607
|
+
}
|
|
83608
|
+
const REVISION = _revision;
|
|
83425
83609
|
// Expose on global so the version can be read in a production browser console via window.__COPPER3D_VERSION__
|
|
83426
83610
|
if (typeof window !== "undefined") {
|
|
83427
83611
|
window.__COPPER3D_VERSION__ = REVISION;
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,6 @@ import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_
|
|
|
28
28
|
import type { LayerId, ChannelValue } from "./Utils/segmentation/core/index";
|
|
29
29
|
import type { AiPromptTool, AiPromptPoint, AiPromptPayload, AiMaskResult } from "./Utils/segmentation/tools/AiAssistTool";
|
|
30
30
|
import "./css/style.css";
|
|
31
|
-
export declare const REVISION
|
|
31
|
+
export declare const REVISION: string;
|
|
32
32
|
export { copperRenderer, copperRendererOnDemond, copperMSceneRenderer, setHDRFilePath, addLabelToScene, convert3DPostoScreenPos, convertScreenPosto3DPos, addBoxHelper, fullScreenListenner, configKiwriousHeart, copperScene, copperSceneOnDemond, copperMScene, CameraViewPoint, kiwrious, NrrdTools, loading, Copper3dTrackballControls, createTexture2D_NRRD, MeshNodeTool, throttle, removeGuiFolderChilden, CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss, GaussianSmoother, SurfaceAnnotator, };
|
|
33
33
|
export type { positionType, screenPosType, optsType, nrrdMeshesType, nrrdSliceType, SensorDecodedValue_kiwrious, SensorReadResult_kiwrious, HeartRateResult_kiwrious, loadingBarType, IPaintImage, exportPaintImageType, IOptVTKLoader, aligned4DSurfaceType, aligned4DOptsType, Aligned4DController, ICommXYZ, IGUIStates, IGuiParameterSettings, INrrdStates, NrrdState, GuiState, IGuiMeta, ToolMode, IAnnotationCallbacks, LayerId, ChannelValue, AiPromptTool, AiPromptPoint, AiPromptPayload, AiMaskResult, SurfaceAnnotatorOptions, Annotation, AnnotationMode, ExportOptions, };
|
package/dist/index.js
CHANGED
|
@@ -22,8 +22,17 @@ import { removeGuiFolderChilden } from "./Utils/segmentation/coreTools/gui";
|
|
|
22
22
|
import { SurfaceAnnotator } from "./Utils/surfaceAnnotation";
|
|
23
23
|
import { CHANNEL_COLORS, CHANNEL_HEX_COLORS, AI_MASK_CHANNEL_COLORS, AI_CHANNEL_HEX_COLORS, rgbaToHex, rgbaToCss } from "./Utils/segmentation/core/index";
|
|
24
24
|
import "./css/style.css";
|
|
25
|
-
// __REVISION__ is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version
|
|
26
|
-
|
|
25
|
+
// __REVISION__ is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version.
|
|
26
|
+
// When copper3d is consumed from local source (no rollup replace step), the identifier is undefined and
|
|
27
|
+
// referencing it throws a ReferenceError — guard it so local loading still works.
|
|
28
|
+
let _revision = "unknown";
|
|
29
|
+
try {
|
|
30
|
+
_revision = __REVISION__;
|
|
31
|
+
}
|
|
32
|
+
catch (_a) {
|
|
33
|
+
/* __REVISION__ not injected (local source build) */
|
|
34
|
+
}
|
|
35
|
+
export const REVISION = _revision;
|
|
27
36
|
// Expose on global so the version can be read in a production browser console via window.__COPPER3D_VERSION__
|
|
28
37
|
if (typeof window !== "undefined") {
|
|
29
38
|
window.__COPPER3D_VERSION__ = REVISION;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,QAAQ,MAAM,iCAAiC,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAC9E,iDAAiD;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AA0B7D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAS1J,OAAO,iBAAiB,CAAC;AAEzB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EACL,eAAe,EAGf,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,QAAQ,MAAM,iCAAiC,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAC9E,iDAAiD;AAEjD,OAAO,EAAE,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAEjF,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AAE5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AA0B7D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AAS1J,OAAO,iBAAiB,CAAC;AAEzB,8GAA8G;AAC9G,wGAAwG;AACxG,kFAAkF;AAClF,IAAI,SAAS,GAAG,SAAS,CAAC;AAC1B,IAAI;IACF,SAAS,GAAG,YAAY,CAAC;CAC1B;AAAC,WAAM;IACN,oDAAoD;CACrD;AACD,MAAM,CAAC,MAAM,QAAQ,GAAG,SAAS,CAAC;AAElC,8GAA8G;AAC9G,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;IAChC,MAAc,CAAC,oBAAoB,GAAG,QAAQ,CAAC;CACjD;AAED,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE;IAClC,OAAO,CAAC,GAAG,CACT,8BAA8B,QAAQ,EAAE,EACxC,8CAA8C,EAC9C,8CAA8C,CAC/C,CAAC;CACH;AAED,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,WAAW,EACX,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,SAAS,EACT,OAAO,EACP,yBAAyB,EACzB,oBAAoB,EACpB,YAAY,EACZ,QAAQ,EACR,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,SAAS,EACT,SAAS,EACT,gBAAgB,EAChB,gBAAgB,GACjB,CAAC"}
|
|
@@ -276,10 +276,24 @@ export declare class NrrdTools {
|
|
|
276
276
|
}[];
|
|
277
277
|
} | null;
|
|
278
278
|
/**
|
|
279
|
-
* Merge the AI scratch into a target layer
|
|
280
|
-
* merge —
|
|
281
|
-
*
|
|
282
|
-
*
|
|
279
|
+
* Merge the AI scratch into a target layer with an explicit per-segmentation
|
|
280
|
+
* mapping, as a single undoable group (sandbox merge — non-destructive + one
|
|
281
|
+
* Ctrl+Z). `mapping` is `{ AI scratch label -> target channel(1-8) }`; any label
|
|
282
|
+
* absent from the mapping (or mapped to 0) is DISCARDED (not merged). Several AI
|
|
283
|
+
* labels MAY map to the SAME channel — after merge the channel is the identity
|
|
284
|
+
* (CLAUDE.md decision M2), so the merged voxels take that channel's fixed colour.
|
|
285
|
+
*
|
|
286
|
+
* UNION semantics (M4): an AI voxel only fills a target voxel that is currently
|
|
287
|
+
* EMPTY (0) — existing manual annotation is NEVER erased. Where two AI labels map
|
|
288
|
+
* to different channels and overlap the same voxel, the first one written wins
|
|
289
|
+
* (iteration order), an accepted edge case for a single-value-per-voxel mask.
|
|
290
|
+
* Scans all z-slices, so voxels painted from any view are caught.
|
|
291
|
+
*/
|
|
292
|
+
aiCommitToLayerMapped(targetLayer: string, mapping: Record<number, number>): void;
|
|
293
|
+
/**
|
|
294
|
+
* Back-compat shim: merge with an IDENTITY mapping (each AI label → the same
|
|
295
|
+
* channel number) via the mapped path above. Newer callers should use
|
|
296
|
+
* `aiCommitToLayerMapped` with an explicit per-segmentation mapping.
|
|
283
297
|
*/
|
|
284
298
|
aiCommitToLayer(targetLayer?: string): void;
|
|
285
299
|
clearActiveLayer(): void;
|
|
@@ -29,6 +29,10 @@ export declare class MeshGraph {
|
|
|
29
29
|
shortestPath(startV: number, endV: number): number[];
|
|
30
30
|
vertexWorld(i: number, matrixWorld: THREE.Matrix4): THREE.Vector3;
|
|
31
31
|
vertexNormalWorld(i: number, mesh: THREE.Mesh): THREE.Vector3;
|
|
32
|
+
/** Vertex local normal at index i (reads from the welded graphGeo normal attribute). */
|
|
33
|
+
vertexNormalLocal(i: number): THREE.Vector3;
|
|
34
|
+
/** Normal at the vertex nearest to localPoint (for recovering normals from imported point coordinates). */
|
|
35
|
+
nearestNormalLocal(p: THREE.Vector3): THREE.Vector3;
|
|
32
36
|
/** Vertex local coordinates + local normal (the graph geometry is already local space). */
|
|
33
37
|
vertexLocal(i: number): {
|
|
34
38
|
x: number;
|
|
@@ -31,6 +31,12 @@ export interface SurfaceAnnotatorOptions {
|
|
|
31
31
|
onModeChange?: (m: AnnotationMode) => void;
|
|
32
32
|
/** Callback when the annotation list changes (add/remove/undo/clear). */
|
|
33
33
|
onChange?: (annotations: Annotation[]) => void;
|
|
34
|
+
/** Callback when interaction state changes (drawing, armed tool, draw-lock status). */
|
|
35
|
+
onInteractionChange?: (s: {
|
|
36
|
+
drawing: boolean;
|
|
37
|
+
armed: AnnotationMode;
|
|
38
|
+
locked: boolean;
|
|
39
|
+
}) => void;
|
|
34
40
|
}
|
|
35
41
|
/**
|
|
36
42
|
* Main surface-annotation controller (Phase 4): four modes navigate / freehand / geodesic / point,
|
|
@@ -42,6 +48,11 @@ export declare class SurfaceAnnotator {
|
|
|
42
48
|
private o;
|
|
43
49
|
private mode;
|
|
44
50
|
private spaceHeld;
|
|
51
|
+
private drawLock;
|
|
52
|
+
private armed;
|
|
53
|
+
private spaceDownAt;
|
|
54
|
+
private spaceDragged;
|
|
55
|
+
private static readonly TAP_MS;
|
|
45
56
|
private pointerDown;
|
|
46
57
|
private readonly markerRadius;
|
|
47
58
|
private readonly epsilon;
|
|
@@ -91,9 +102,29 @@ export declare class SurfaceAnnotator {
|
|
|
91
102
|
label: string;
|
|
92
103
|
color: string;
|
|
93
104
|
closed: boolean;
|
|
105
|
+
visible: boolean;
|
|
94
106
|
points: number[][];
|
|
95
107
|
}[];
|
|
96
108
|
};
|
|
109
|
+
setVisible(id: string, visible: boolean): void;
|
|
110
|
+
/**
|
|
111
|
+
* Rebuild annotations from an exported payload (local-space points). Normals are taken from the
|
|
112
|
+
* point when present ([x,y,z,nx,ny,nz]); otherwise recovered from the welded graph's nearest
|
|
113
|
+
* vertex. Each imported item becomes first-class (select/recolor/hide/delete/export).
|
|
114
|
+
*/
|
|
115
|
+
importAnnotations(payload: {
|
|
116
|
+
annotations: Array<{
|
|
117
|
+
id?: string;
|
|
118
|
+
type: "contour" | "points";
|
|
119
|
+
mode: "freehand" | "geodesic" | null;
|
|
120
|
+
label: string;
|
|
121
|
+
color: string;
|
|
122
|
+
closed: boolean;
|
|
123
|
+
visible?: boolean;
|
|
124
|
+
points: number[][];
|
|
125
|
+
}>;
|
|
126
|
+
}): number;
|
|
127
|
+
private get drawing();
|
|
97
128
|
private applyCameraGating;
|
|
98
129
|
/** Reconcile the scene against store.list(): add missing objects, remove deleted ones (no dispose, kept for undo restore). */
|
|
99
130
|
private reconcile;
|
|
@@ -20,6 +20,7 @@ export declare class AnnotationStore {
|
|
|
20
20
|
get(id: string): Annotation | undefined;
|
|
21
21
|
setLabel(id: string, label: string): void;
|
|
22
22
|
setColor(id: string, color: string): void;
|
|
23
|
+
setVisible(id: string, visible: boolean): void;
|
|
23
24
|
toJSON(modelName: string, mesh: THREE.Mesh, opts?: ExportOptions): {
|
|
24
25
|
model: string;
|
|
25
26
|
exportedAt: string;
|
|
@@ -31,6 +32,7 @@ export declare class AnnotationStore {
|
|
|
31
32
|
label: string;
|
|
32
33
|
color: string;
|
|
33
34
|
closed: boolean;
|
|
35
|
+
visible: boolean;
|
|
34
36
|
points: number[][];
|
|
35
37
|
}[];
|
|
36
38
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copper3d",
|
|
3
3
|
"description": "A 3d visualisation package base on threejs provides multiple scenes and Nrrd image load funtion.",
|
|
4
|
-
"version": "3.6.
|
|
4
|
+
"version": "3.6.7",
|
|
5
5
|
"main": "dist/bundle.umd.js",
|
|
6
6
|
"moudle": "dist/bundle.esm.js",
|
|
7
7
|
"types": "dist/types/index.d.ts",
|