copper3d 3.6.6 → 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/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 +159 -16
- package/dist/bundle.umd.js +159 -16
- 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.esm.js
CHANGED
|
@@ -61165,6 +61165,15 @@ class MeshGraph {
|
|
|
61165
61165
|
const nm = new Matrix3().getNormalMatrix(mesh.matrixWorld);
|
|
61166
61166
|
return n.applyMatrix3(nm).normalize();
|
|
61167
61167
|
}
|
|
61168
|
+
/** Vertex local normal at index i (reads from the welded graphGeo normal attribute). */
|
|
61169
|
+
vertexNormalLocal(i) {
|
|
61170
|
+
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);
|
|
61171
|
+
}
|
|
61172
|
+
/** Normal at the vertex nearest to localPoint (for recovering normals from imported point coordinates). */
|
|
61173
|
+
nearestNormalLocal(p) {
|
|
61174
|
+
const i = this.nearestVertex(p);
|
|
61175
|
+
return this.vertexNormalLocal(i);
|
|
61176
|
+
}
|
|
61168
61177
|
/** Vertex local coordinates + local normal (the graph geometry is already local space). */
|
|
61169
61178
|
vertexLocal(i) {
|
|
61170
61179
|
return {
|
|
@@ -61390,6 +61399,13 @@ class AnnotationStore {
|
|
|
61390
61399
|
this.notify();
|
|
61391
61400
|
}
|
|
61392
61401
|
}
|
|
61402
|
+
setVisible(id, visible) {
|
|
61403
|
+
const a = this.get(id);
|
|
61404
|
+
if (a) {
|
|
61405
|
+
a.visible = visible;
|
|
61406
|
+
this.notify();
|
|
61407
|
+
}
|
|
61408
|
+
}
|
|
61393
61409
|
toJSON(modelName, mesh, opts = {}) {
|
|
61394
61410
|
var _a;
|
|
61395
61411
|
const space = (_a = opts.space) !== null && _a !== void 0 ? _a : "local";
|
|
@@ -61414,6 +61430,7 @@ class AnnotationStore {
|
|
|
61414
61430
|
label: a.label,
|
|
61415
61431
|
color: a.color,
|
|
61416
61432
|
closed: a.closed,
|
|
61433
|
+
visible: a.visible,
|
|
61417
61434
|
points: a.vertices.map(toPt),
|
|
61418
61435
|
})),
|
|
61419
61436
|
};
|
|
@@ -61433,6 +61450,10 @@ class SurfaceAnnotator {
|
|
|
61433
61450
|
var _a, _b, _c;
|
|
61434
61451
|
this.mode = "navigate";
|
|
61435
61452
|
this.spaceHeld = false;
|
|
61453
|
+
this.drawLock = false;
|
|
61454
|
+
this.armed = "freehand";
|
|
61455
|
+
this.spaceDownAt = 0;
|
|
61456
|
+
this.spaceDragged = false;
|
|
61436
61457
|
this.pointerDown = false;
|
|
61437
61458
|
this.store = new AnnotationStore();
|
|
61438
61459
|
this.managed = new Set();
|
|
@@ -61459,14 +61480,18 @@ class SurfaceAnnotator {
|
|
|
61459
61480
|
(this.activeGeoLine.material.resolution.set(w, h));
|
|
61460
61481
|
};
|
|
61461
61482
|
this.onPointerDown = (e) => {
|
|
61462
|
-
if (this.spaceHeld)
|
|
61463
|
-
return;
|
|
61464
61483
|
if (e.button !== 0)
|
|
61465
61484
|
return; // left button only
|
|
61466
61485
|
if (!this.insideContainer(e))
|
|
61467
61486
|
return;
|
|
61468
61487
|
this.pointerDown = true;
|
|
61469
|
-
|
|
61488
|
+
// Only act with the armed tool when in drawing mode (drawLock or spaceHeld).
|
|
61489
|
+
// When not drawing, navigation is the default — pointer events go to camera controls.
|
|
61490
|
+
if (!this.drawing)
|
|
61491
|
+
return;
|
|
61492
|
+
// Use armed tool (not this.mode which may be "navigate")
|
|
61493
|
+
const activeTool = this.armed;
|
|
61494
|
+
if (activeTool === "point") {
|
|
61470
61495
|
const h = this.hit(e);
|
|
61471
61496
|
if (!h)
|
|
61472
61497
|
return;
|
|
@@ -61480,13 +61505,14 @@ class SurfaceAnnotator {
|
|
|
61480
61505
|
label: `Point ${this.seq}`,
|
|
61481
61506
|
color: this.pointColorVal,
|
|
61482
61507
|
closed: false,
|
|
61508
|
+
visible: true,
|
|
61483
61509
|
vertices: [v],
|
|
61484
61510
|
object3D: marker,
|
|
61485
61511
|
};
|
|
61486
61512
|
this.store.add(ann);
|
|
61487
61513
|
return;
|
|
61488
61514
|
}
|
|
61489
|
-
if (
|
|
61515
|
+
if (activeTool === "freehand") {
|
|
61490
61516
|
this.activeStroke = new StrokeContour(this.minGap, this.maxJump, this.o.mesh);
|
|
61491
61517
|
this.activeStroke.begin();
|
|
61492
61518
|
const h = this.hit(e);
|
|
@@ -61496,7 +61522,7 @@ class SurfaceAnnotator {
|
|
|
61496
61522
|
this.o.scene.add(this.activeLine);
|
|
61497
61523
|
return;
|
|
61498
61524
|
}
|
|
61499
|
-
if (
|
|
61525
|
+
if (activeTool === "geodesic") {
|
|
61500
61526
|
// First check whether an existing anchor was clicked → cancel that point (any point can be canceled).
|
|
61501
61527
|
const pick = this.pickGeoMarker(e);
|
|
61502
61528
|
if (pick >= 0 && this.activeGeo) {
|
|
@@ -61523,15 +61549,26 @@ class SurfaceAnnotator {
|
|
|
61523
61549
|
}
|
|
61524
61550
|
};
|
|
61525
61551
|
this.onPointerMove = (e) => {
|
|
61526
|
-
if (
|
|
61527
|
-
|
|
61552
|
+
// Track if user dragged while space was held (so a hold-drag is not treated as a tap).
|
|
61553
|
+
if (this.spaceHeld && this.pointerDown) {
|
|
61554
|
+
this.spaceDragged = true;
|
|
61555
|
+
}
|
|
61556
|
+
// When not drawing, navigation is default — don't interfere with camera controls.
|
|
61557
|
+
if (!this.drawing) {
|
|
61558
|
+
// In geodesic mode we can still show the hover hint when not drawing.
|
|
61559
|
+
if (this.armed === "geodesic" && !this.pointerDown) {
|
|
61560
|
+
this.setGeoHover(this.insideContainer(e) ? this.pickGeoMarker(e) : -1);
|
|
61561
|
+
}
|
|
61562
|
+
else {
|
|
61563
|
+
this.setGeoHover(-1);
|
|
61564
|
+
}
|
|
61528
61565
|
return;
|
|
61529
61566
|
}
|
|
61530
|
-
//
|
|
61531
|
-
if (this.
|
|
61567
|
+
// Drawing mode: geodesic hover hint
|
|
61568
|
+
if (this.armed === "geodesic" && !this.pointerDown) {
|
|
61532
61569
|
this.setGeoHover(this.insideContainer(e) ? this.pickGeoMarker(e) : -1);
|
|
61533
61570
|
}
|
|
61534
|
-
if (this.
|
|
61571
|
+
if (this.armed === "freehand" &&
|
|
61535
61572
|
this.pointerDown &&
|
|
61536
61573
|
this.activeStroke &&
|
|
61537
61574
|
this.activeLine) {
|
|
@@ -61556,6 +61593,7 @@ class SurfaceAnnotator {
|
|
|
61556
61593
|
label: `Contour ${this.seq}`,
|
|
61557
61594
|
color: this.freehandColor,
|
|
61558
61595
|
closed: false,
|
|
61596
|
+
visible: true,
|
|
61559
61597
|
vertices: verts,
|
|
61560
61598
|
object3D: this.activeLine,
|
|
61561
61599
|
};
|
|
@@ -61573,11 +61611,17 @@ class SurfaceAnnotator {
|
|
|
61573
61611
|
if (this.isTypingTarget(e))
|
|
61574
61612
|
return;
|
|
61575
61613
|
if (e.code === "Space") {
|
|
61614
|
+
if (!this.spaceHeld) {
|
|
61615
|
+
this.spaceDownAt = performance.now();
|
|
61616
|
+
this.spaceDragged = false;
|
|
61617
|
+
}
|
|
61576
61618
|
this.spaceHeld = true;
|
|
61577
61619
|
this.applyCameraGating();
|
|
61620
|
+
e.preventDefault();
|
|
61578
61621
|
return;
|
|
61579
61622
|
}
|
|
61580
61623
|
if (e.key === "Escape") {
|
|
61624
|
+
this.drawLock = false;
|
|
61581
61625
|
this.setMode("navigate");
|
|
61582
61626
|
return;
|
|
61583
61627
|
}
|
|
@@ -61610,7 +61654,11 @@ class SurfaceAnnotator {
|
|
|
61610
61654
|
};
|
|
61611
61655
|
this.onKeyUp = (e) => {
|
|
61612
61656
|
if (e.code === "Space") {
|
|
61657
|
+
const held = performance.now() - this.spaceDownAt;
|
|
61613
61658
|
this.spaceHeld = false;
|
|
61659
|
+
if (held <= SurfaceAnnotator.TAP_MS && !this.spaceDragged) {
|
|
61660
|
+
this.drawLock = !this.drawLock; // tap toggles lock
|
|
61661
|
+
}
|
|
61614
61662
|
this.applyCameraGating();
|
|
61615
61663
|
}
|
|
61616
61664
|
};
|
|
@@ -61666,6 +61714,10 @@ class SurfaceAnnotator {
|
|
|
61666
61714
|
if (m !== "geodesic" && this.activeGeo)
|
|
61667
61715
|
this.clearActiveGeo();
|
|
61668
61716
|
this.mode = m;
|
|
61717
|
+
// Record armed tool when choosing a drawing mode (not navigate).
|
|
61718
|
+
// Camera gating is NOT changed here — the user must use Space to enter drawing.
|
|
61719
|
+
if (m !== "navigate")
|
|
61720
|
+
this.armed = m;
|
|
61669
61721
|
this.applyCameraGating();
|
|
61670
61722
|
(_b = (_a = this.o).onModeChange) === null || _b === void 0 ? void 0 : _b.call(_a, m);
|
|
61671
61723
|
}
|
|
@@ -61728,10 +61780,92 @@ class SurfaceAnnotator {
|
|
|
61728
61780
|
exportJSON(modelName, opts) {
|
|
61729
61781
|
return this.store.toJSON(modelName, this.o.mesh, opts);
|
|
61730
61782
|
}
|
|
61783
|
+
setVisible(id, visible) {
|
|
61784
|
+
this.store.setVisible(id, visible);
|
|
61785
|
+
}
|
|
61786
|
+
/**
|
|
61787
|
+
* Rebuild annotations from an exported payload (local-space points). Normals are taken from the
|
|
61788
|
+
* point when present ([x,y,z,nx,ny,nz]); otherwise recovered from the welded graph's nearest
|
|
61789
|
+
* vertex. Each imported item becomes first-class (select/recolor/hide/delete/export).
|
|
61790
|
+
*/
|
|
61791
|
+
importAnnotations(payload) {
|
|
61792
|
+
var _a, _b, _c, _d;
|
|
61793
|
+
let count = 0;
|
|
61794
|
+
let maxImported = this.seq;
|
|
61795
|
+
for (const a of (_a = payload.annotations) !== null && _a !== void 0 ? _a : []) {
|
|
61796
|
+
const verts = a.points.map((p) => {
|
|
61797
|
+
const [x, y, z] = p;
|
|
61798
|
+
let nx = p[3], ny = p[4], nz = p[5];
|
|
61799
|
+
if (nx === undefined || ny === undefined || nz === undefined) {
|
|
61800
|
+
const nrm = this.graph.nearestNormalLocal(new Vector3(x, y, z));
|
|
61801
|
+
nx = nrm.x;
|
|
61802
|
+
ny = nrm.y;
|
|
61803
|
+
nz = nrm.z;
|
|
61804
|
+
}
|
|
61805
|
+
return { x, y, z, nx, ny, nz, faceIndex: 0 };
|
|
61806
|
+
});
|
|
61807
|
+
if (a.type === "points") {
|
|
61808
|
+
for (const v of verts) {
|
|
61809
|
+
// Only reuse the provided id for a single-point entry (the round-trip case);
|
|
61810
|
+
// a multi-point points entry must get a fresh id per marker to avoid duplicate keys.
|
|
61811
|
+
const id = a.id && verts.length === 1 ? a.id : this.nextId();
|
|
61812
|
+
const marker = makePointMarker(v, this.o.mesh, a.color, this.markerRadius);
|
|
61813
|
+
this.o.scene.add(marker);
|
|
61814
|
+
this.store.add({
|
|
61815
|
+
id,
|
|
61816
|
+
type: "points",
|
|
61817
|
+
mode: null,
|
|
61818
|
+
label: a.label,
|
|
61819
|
+
color: a.color,
|
|
61820
|
+
closed: false,
|
|
61821
|
+
visible: (_b = a.visible) !== null && _b !== void 0 ? _b : true,
|
|
61822
|
+
vertices: [v],
|
|
61823
|
+
object3D: marker,
|
|
61824
|
+
});
|
|
61825
|
+
// track max numeric id
|
|
61826
|
+
const m = id.match(/^a(\d+)$/);
|
|
61827
|
+
if (m)
|
|
61828
|
+
maxImported = Math.max(maxImported, parseInt(m[1], 10));
|
|
61829
|
+
count++;
|
|
61830
|
+
}
|
|
61831
|
+
}
|
|
61832
|
+
else {
|
|
61833
|
+
if (verts.length < 2)
|
|
61834
|
+
continue;
|
|
61835
|
+
const id = (_c = a.id) !== null && _c !== void 0 ? _c : this.nextId();
|
|
61836
|
+
const line = makeContourLine(verts, a.color, a.closed, this.o.container, this.epsilon, this.o.mesh);
|
|
61837
|
+
this.o.scene.add(line);
|
|
61838
|
+
this.store.add({
|
|
61839
|
+
id,
|
|
61840
|
+
type: "contour",
|
|
61841
|
+
mode: a.mode,
|
|
61842
|
+
label: a.label,
|
|
61843
|
+
color: a.color,
|
|
61844
|
+
closed: a.closed,
|
|
61845
|
+
visible: (_d = a.visible) !== null && _d !== void 0 ? _d : true,
|
|
61846
|
+
vertices: verts,
|
|
61847
|
+
object3D: line,
|
|
61848
|
+
});
|
|
61849
|
+
const m = id.match(/^a(\d+)$/);
|
|
61850
|
+
if (m)
|
|
61851
|
+
maxImported = Math.max(maxImported, parseInt(m[1], 10));
|
|
61852
|
+
count++;
|
|
61853
|
+
}
|
|
61854
|
+
}
|
|
61855
|
+
// keep seq ahead of any imported numeric ids to avoid collisions
|
|
61856
|
+
if (maxImported > this.seq)
|
|
61857
|
+
this.seq = maxImported;
|
|
61858
|
+
return count;
|
|
61859
|
+
}
|
|
61731
61860
|
// ---- Internal ----
|
|
61861
|
+
get drawing() {
|
|
61862
|
+
return this.drawLock || this.spaceHeld;
|
|
61863
|
+
}
|
|
61732
61864
|
applyCameraGating() {
|
|
61733
|
-
|
|
61734
|
-
|
|
61865
|
+
var _a, _b;
|
|
61866
|
+
// Default is navigate (camera enabled). Drawing only when drawLock or spaceHeld.
|
|
61867
|
+
this.o.controls.enabled = !this.drawing;
|
|
61868
|
+
(_b = (_a = this.o).onInteractionChange) === null || _b === void 0 ? void 0 : _b.call(_a, { drawing: this.drawing, armed: this.armed, locked: this.drawLock });
|
|
61735
61869
|
}
|
|
61736
61870
|
/** Reconcile the scene against store.list(): add missing objects, remove deleted ones (no dispose, kept for undo restore). */
|
|
61737
61871
|
reconcile() {
|
|
@@ -61749,6 +61883,11 @@ class SurfaceAnnotator {
|
|
|
61749
61883
|
this.o.scene.add(o);
|
|
61750
61884
|
}
|
|
61751
61885
|
this.managed = present;
|
|
61886
|
+
// Apply per-annotation visibility
|
|
61887
|
+
for (const a of this.store.list()) {
|
|
61888
|
+
if (a.object3D)
|
|
61889
|
+
a.object3D.visible = a.visible;
|
|
61890
|
+
}
|
|
61752
61891
|
this.applySelection();
|
|
61753
61892
|
(_b = (_a = this.o).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, this.store.list());
|
|
61754
61893
|
}
|
|
@@ -61756,6 +61895,8 @@ class SurfaceAnnotator {
|
|
|
61756
61895
|
for (const a of this.store.list()) {
|
|
61757
61896
|
if (!a.object3D)
|
|
61758
61897
|
continue;
|
|
61898
|
+
if (!a.visible)
|
|
61899
|
+
continue;
|
|
61759
61900
|
const sel = a.id === this.selectedId;
|
|
61760
61901
|
if (a.type === "contour") {
|
|
61761
61902
|
const mat = a.object3D.material;
|
|
@@ -61988,6 +62129,7 @@ class SurfaceAnnotator {
|
|
|
61988
62129
|
label: `Contour ${this.seq}`,
|
|
61989
62130
|
color: this.geodesicColor,
|
|
61990
62131
|
closed,
|
|
62132
|
+
visible: true,
|
|
61991
62133
|
vertices: verts,
|
|
61992
62134
|
object3D: this.activeGeoLine,
|
|
61993
62135
|
};
|
|
@@ -62021,7 +62163,8 @@ class SurfaceAnnotator {
|
|
|
62021
62163
|
window.removeEventListener("keyup", this.onKeyUp);
|
|
62022
62164
|
window.removeEventListener("resize", this.onResize);
|
|
62023
62165
|
}
|
|
62024
|
-
}
|
|
62166
|
+
}
|
|
62167
|
+
SurfaceAnnotator.TAP_MS = 250;
|
|
62025
62168
|
|
|
62026
62169
|
class copperScene extends baseScene {
|
|
62027
62170
|
// rayster pick
|
|
@@ -83444,15 +83587,15 @@ function evaluateElement(element, weights) {
|
|
|
83444
83587
|
}
|
|
83445
83588
|
|
|
83446
83589
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
83447
|
-
// "v3.6.
|
|
83590
|
+
// "v3.6.7" is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version.
|
|
83448
83591
|
// When copper3d is consumed from local source (no rollup replace step), the identifier is undefined and
|
|
83449
83592
|
// referencing it throws a ReferenceError — guard it so local loading still works.
|
|
83450
83593
|
let _revision = "unknown";
|
|
83451
83594
|
try {
|
|
83452
|
-
_revision = "v3.6.
|
|
83595
|
+
_revision = "v3.6.7";
|
|
83453
83596
|
}
|
|
83454
83597
|
catch (_a) {
|
|
83455
|
-
/* "v3.6.
|
|
83598
|
+
/* "v3.6.7" not injected (local source build) */
|
|
83456
83599
|
}
|
|
83457
83600
|
const REVISION = _revision;
|
|
83458
83601
|
// Expose on global so the version can be read in a production browser console via window.__COPPER3D_VERSION__
|
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
|
|
@@ -83452,15 +83595,15 @@ void main() {
|
|
|
83452
83595
|
}
|
|
83453
83596
|
|
|
83454
83597
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
83455
|
-
// "v3.6.
|
|
83598
|
+
// "v3.6.7" is injected at build time by rollup @rollup/plugin-replace, sourced from package.json version.
|
|
83456
83599
|
// When copper3d is consumed from local source (no rollup replace step), the identifier is undefined and
|
|
83457
83600
|
// referencing it throws a ReferenceError — guard it so local loading still works.
|
|
83458
83601
|
let _revision = "unknown";
|
|
83459
83602
|
try {
|
|
83460
|
-
_revision = "v3.6.
|
|
83603
|
+
_revision = "v3.6.7";
|
|
83461
83604
|
}
|
|
83462
83605
|
catch (_a) {
|
|
83463
|
-
/* "v3.6.
|
|
83606
|
+
/* "v3.6.7" not injected (local source build) */
|
|
83464
83607
|
}
|
|
83465
83608
|
const REVISION = _revision;
|
|
83466
83609
|
// Expose on global so the version can be read in a production browser console via window.__COPPER3D_VERSION__
|
|
@@ -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;
|