copper3d 3.6.0 → 3.6.2
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/SurfaceAnnotator.d.ts +29 -1
- package/dist/Utils/surfaceAnnotation/SurfaceAnnotator.js +198 -13
- package/dist/Utils/surfaceAnnotation/SurfaceAnnotator.js.map +1 -1
- package/dist/Utils/surfaceAnnotation/geodesicContour.d.ts +13 -0
- package/dist/Utils/surfaceAnnotation/geodesicContour.js +38 -0
- package/dist/Utils/surfaceAnnotation/geodesicContour.js.map +1 -1
- package/dist/Utils/surfaceAnnotation/strokeContour.d.ts +8 -1
- package/dist/Utils/surfaceAnnotation/strokeContour.js +19 -4
- package/dist/Utils/surfaceAnnotation/strokeContour.js.map +1 -1
- package/dist/bundle.esm.js +256 -18
- package/dist/bundle.umd.js +256 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types/Utils/surfaceAnnotation/SurfaceAnnotator.d.ts +29 -1
- package/dist/types/Utils/surfaceAnnotation/geodesicContour.d.ts +13 -0
- package/dist/types/Utils/surfaceAnnotation/strokeContour.d.ts +8 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/bundle.umd.js
CHANGED
|
@@ -59589,11 +59589,18 @@ void main() {
|
|
|
59589
59589
|
* 顶点以 local 存(由 mesh 把世界命中点转 local)。
|
|
59590
59590
|
*/
|
|
59591
59591
|
class StrokeContour {
|
|
59592
|
-
|
|
59592
|
+
/**
|
|
59593
|
+
* @param minGap 采样最小间距(世界系),去抖
|
|
59594
|
+
* @param maxJump 跳变阈值(世界系):距离 > maxJump 且法线大幅翻转时丢弃该样本,
|
|
59595
|
+
* 避免笔触掠过沟缝/轮廓边时相邻点落在不同深度、直线段横穿模型。
|
|
59596
|
+
*/
|
|
59597
|
+
constructor(minGap, maxJump, mesh) {
|
|
59593
59598
|
this.minGap = minGap;
|
|
59599
|
+
this.maxJump = maxJump;
|
|
59594
59600
|
this.mesh = mesh;
|
|
59595
59601
|
this.verts = [];
|
|
59596
59602
|
this.last = new Vector3();
|
|
59603
|
+
this.lastNormal = new Vector3();
|
|
59597
59604
|
this.has = false;
|
|
59598
59605
|
}
|
|
59599
59606
|
begin() {
|
|
@@ -59601,11 +59608,19 @@ void main() {
|
|
|
59601
59608
|
this.has = false;
|
|
59602
59609
|
}
|
|
59603
59610
|
addSample(hit) {
|
|
59604
|
-
|
|
59605
|
-
|
|
59606
|
-
|
|
59611
|
+
if (this.has) {
|
|
59612
|
+
const d = hit.point.distanceTo(this.last);
|
|
59613
|
+
if (d < this.minGap)
|
|
59614
|
+
return; // 太近,去抖
|
|
59615
|
+
// 跳变剔除:距离突然很大「且」法线大幅翻转(>~75°)→ 多半跳到了背面/远面,丢弃,
|
|
59616
|
+
// 否则相邻两点之间的直线段会横穿模型内部。两个条件同时满足才剔除:
|
|
59617
|
+
// 快速画(距离大、法线相近)不误删;画过尖锐棱边(法线变、距离近)也不误删。
|
|
59618
|
+
if (d > this.maxJump && hit.normal.dot(this.lastNormal) < 0.25)
|
|
59619
|
+
return;
|
|
59620
|
+
}
|
|
59607
59621
|
this.verts.push(worldHitToLocalVertex(hit, this.mesh));
|
|
59608
59622
|
this.last.copy(hit.point);
|
|
59623
|
+
this.lastNormal.copy(hit.normal);
|
|
59609
59624
|
this.has = true;
|
|
59610
59625
|
}
|
|
59611
59626
|
get vertices() {
|
|
@@ -61155,16 +61170,54 @@ void main() {
|
|
|
61155
61170
|
this.mesh = mesh;
|
|
61156
61171
|
this.anchors = []; // 顶点索引
|
|
61157
61172
|
this.segments = []; // 相邻锚点间路径(含端点)
|
|
61173
|
+
this.history = []; // 每次增删前的锚点快照,用于撤销
|
|
61174
|
+
}
|
|
61175
|
+
/** 任何改动锚点前先存一份快照(顶点索引数组很小,快照成本可忽略)。 */
|
|
61176
|
+
snapshot() {
|
|
61177
|
+
this.history.push(this.anchors.slice());
|
|
61158
61178
|
}
|
|
61159
61179
|
/** 传入 local 命中点,snap 到最近顶点并对上一锚点求路径。 */
|
|
61160
61180
|
addAnchor(localHitPoint) {
|
|
61161
61181
|
const v = this.graph.nearestVertex(localHitPoint);
|
|
61182
|
+
this.snapshot();
|
|
61162
61183
|
if (this.anchors.length > 0) {
|
|
61163
61184
|
const prev = this.anchors[this.anchors.length - 1];
|
|
61164
61185
|
this.segments.push(this.graph.shortestPath(prev, v));
|
|
61165
61186
|
}
|
|
61166
61187
|
this.anchors.push(v);
|
|
61167
61188
|
}
|
|
61189
|
+
/** 删除指定下标的锚点,重算相邻段(支持取消中间任意一点)。 */
|
|
61190
|
+
removeAnchorAt(index) {
|
|
61191
|
+
if (index < 0 || index >= this.anchors.length)
|
|
61192
|
+
return;
|
|
61193
|
+
this.snapshot();
|
|
61194
|
+
this.anchors.splice(index, 1);
|
|
61195
|
+
this.rebuildSegments();
|
|
61196
|
+
}
|
|
61197
|
+
/** 是否还有可撤销的编辑(加点/删点)。 */
|
|
61198
|
+
canUndo() {
|
|
61199
|
+
return this.history.length > 0;
|
|
61200
|
+
}
|
|
61201
|
+
/** 撤销上一次锚点编辑(加点 → 删回去;删点 → 恢复)。无历史返回 false。 */
|
|
61202
|
+
undoEdit() {
|
|
61203
|
+
const prev = this.history.pop();
|
|
61204
|
+
if (!prev)
|
|
61205
|
+
return false;
|
|
61206
|
+
this.anchors = prev;
|
|
61207
|
+
this.rebuildSegments();
|
|
61208
|
+
return true;
|
|
61209
|
+
}
|
|
61210
|
+
/** 据当前锚点序列整体重算所有相邻段路径。 */
|
|
61211
|
+
rebuildSegments() {
|
|
61212
|
+
this.segments = [];
|
|
61213
|
+
for (let i = 1; i < this.anchors.length; i++) {
|
|
61214
|
+
this.segments.push(this.graph.shortestPath(this.anchors[i - 1], this.anchors[i]));
|
|
61215
|
+
}
|
|
61216
|
+
}
|
|
61217
|
+
/** 各锚点的 local 顶点(用于绘制可见的锚点标记)。 */
|
|
61218
|
+
getAnchorLocals() {
|
|
61219
|
+
return this.anchors.map((i) => this.graph.vertexLocal(i));
|
|
61220
|
+
}
|
|
61168
61221
|
get anchorCount() {
|
|
61169
61222
|
return this.anchors.length;
|
|
61170
61223
|
}
|
|
@@ -61309,6 +61362,11 @@ void main() {
|
|
|
61309
61362
|
this.managed = new Set();
|
|
61310
61363
|
this.seq = 0;
|
|
61311
61364
|
this.selectedId = null;
|
|
61365
|
+
this.activeGeoMarkers = []; // 进行中测地线的可见锚点
|
|
61366
|
+
this.geoRay = new Raycaster();
|
|
61367
|
+
this.geoNdc = new Vector2();
|
|
61368
|
+
this.hoveredGeoMarker = -1;
|
|
61369
|
+
this._projV = new Vector3();
|
|
61312
61370
|
/** 窗口尺寸变化时更新所有 fat line 的像素分辨率,否则线宽会失真。 */
|
|
61313
61371
|
this.onResize = () => {
|
|
61314
61372
|
const w = this.o.container.clientWidth;
|
|
@@ -61353,7 +61411,7 @@ void main() {
|
|
|
61353
61411
|
return;
|
|
61354
61412
|
}
|
|
61355
61413
|
if (this.mode === "freehand") {
|
|
61356
|
-
this.activeStroke = new StrokeContour(this.minGap, this.o.mesh);
|
|
61414
|
+
this.activeStroke = new StrokeContour(this.minGap, this.maxJump, this.o.mesh);
|
|
61357
61415
|
this.activeStroke.begin();
|
|
61358
61416
|
const h = this.hit(e);
|
|
61359
61417
|
if (h)
|
|
@@ -61363,6 +61421,19 @@ void main() {
|
|
|
61363
61421
|
return;
|
|
61364
61422
|
}
|
|
61365
61423
|
if (this.mode === "geodesic") {
|
|
61424
|
+
// 先判断是否点中了一个已有锚点 → 取消该点(支持取消其中任意一点)。
|
|
61425
|
+
const pick = this.pickGeoMarker(e);
|
|
61426
|
+
if (pick >= 0 && this.activeGeo) {
|
|
61427
|
+
this.activeGeo.removeAnchorAt(pick);
|
|
61428
|
+
if (this.activeGeo.anchorCount === 0)
|
|
61429
|
+
this.clearActiveGeo();
|
|
61430
|
+
else {
|
|
61431
|
+
this.rebuildGeoMarkers();
|
|
61432
|
+
this.redrawGeoLine();
|
|
61433
|
+
}
|
|
61434
|
+
return;
|
|
61435
|
+
}
|
|
61436
|
+
// 否则在表面落一个新锚点。
|
|
61366
61437
|
const h = this.hit(e);
|
|
61367
61438
|
if (!h)
|
|
61368
61439
|
return;
|
|
@@ -61371,19 +61442,19 @@ void main() {
|
|
|
61371
61442
|
}
|
|
61372
61443
|
const local = this.o.mesh.worldToLocal(h.point.clone());
|
|
61373
61444
|
this.activeGeo.addAnchor(local);
|
|
61374
|
-
|
|
61375
|
-
|
|
61376
|
-
this.activeGeoLine = makeContourLine(verts, this.geodesicColor, false, this.o.container, this.epsilon, this.o.mesh);
|
|
61377
|
-
this.o.scene.add(this.activeGeoLine);
|
|
61378
|
-
}
|
|
61379
|
-
else {
|
|
61380
|
-
updateContourLine(this.activeGeoLine, verts, false, this.epsilon, this.o.mesh);
|
|
61381
|
-
}
|
|
61445
|
+
this.rebuildGeoMarkers();
|
|
61446
|
+
this.redrawGeoLine();
|
|
61382
61447
|
}
|
|
61383
61448
|
};
|
|
61384
61449
|
this.onPointerMove = (e) => {
|
|
61385
|
-
if (this.spaceHeld)
|
|
61450
|
+
if (this.spaceHeld) {
|
|
61451
|
+
this.setGeoHover(-1);
|
|
61386
61452
|
return;
|
|
61453
|
+
}
|
|
61454
|
+
// 测地线模式下,悬停到锚点小球时显示"✕"提示(该点可被点击取消)。
|
|
61455
|
+
if (this.mode === "geodesic" && !this.pointerDown) {
|
|
61456
|
+
this.setGeoHover(this.insideContainer(e) ? this.pickGeoMarker(e) : -1);
|
|
61457
|
+
}
|
|
61387
61458
|
if (this.mode === "freehand" &&
|
|
61388
61459
|
this.pointerDown &&
|
|
61389
61460
|
this.activeStroke &&
|
|
@@ -61473,9 +61544,10 @@ void main() {
|
|
|
61473
61544
|
meshGeo.computeVertexNormals(); // 渲染需要法线
|
|
61474
61545
|
meshGeo.computeBoundingBox();
|
|
61475
61546
|
const diag = (_a = opts.bboxDiagonal) !== null && _a !== void 0 ? _a : meshGeo.boundingBox.getSize(new Vector3()).length();
|
|
61476
|
-
this.markerRadius = (_b = opts.markerRadius) !== null && _b !== void 0 ? _b : diag * 0.
|
|
61547
|
+
this.markerRadius = (_b = opts.markerRadius) !== null && _b !== void 0 ? _b : diag * 0.0032;
|
|
61477
61548
|
this.epsilon = diag * 0.002;
|
|
61478
61549
|
this.minGap = diag * 0.004;
|
|
61550
|
+
this.maxJump = diag * 0.05;
|
|
61479
61551
|
// 测地线连通性:单独焊接一份"仅位置"的几何给 MeshGraph 用——
|
|
61480
61552
|
// 不动被渲染的 mesh(保留其法线/UV/纹理)。仅位置可保证同表面位置的顶点
|
|
61481
61553
|
// 被 mergeVertices 合并(否则逐面法线/UV 会阻止合并,图断裂 → 闭合穿模)。
|
|
@@ -61513,6 +61585,9 @@ void main() {
|
|
|
61513
61585
|
}
|
|
61514
61586
|
setMode(m) {
|
|
61515
61587
|
var _a, _b;
|
|
61588
|
+
// 离开测地线模式时丢弃尚未 Enter 落定的进行中测地线(清掉残留的锚点与线)。
|
|
61589
|
+
if (m !== "geodesic" && this.activeGeo)
|
|
61590
|
+
this.clearActiveGeo();
|
|
61516
61591
|
this.mode = m;
|
|
61517
61592
|
this.applyCameraGating();
|
|
61518
61593
|
(_b = (_a = this.o).onModeChange) === null || _b === void 0 ? void 0 : _b.call(_a, m);
|
|
@@ -61525,6 +61600,18 @@ void main() {
|
|
|
61525
61600
|
return this.store.list();
|
|
61526
61601
|
}
|
|
61527
61602
|
undo() {
|
|
61603
|
+
// 测地线进行中 → 撤销最近一次锚点编辑(加点 / 取消点都能回退);
|
|
61604
|
+
// 否则撤销最近一条已落定的标注。
|
|
61605
|
+
if (this.activeGeo && this.activeGeo.canUndo()) {
|
|
61606
|
+
this.activeGeo.undoEdit();
|
|
61607
|
+
if (this.activeGeo.anchorCount === 0)
|
|
61608
|
+
this.clearActiveGeo();
|
|
61609
|
+
else {
|
|
61610
|
+
this.rebuildGeoMarkers();
|
|
61611
|
+
this.redrawGeoLine();
|
|
61612
|
+
}
|
|
61613
|
+
return;
|
|
61614
|
+
}
|
|
61528
61615
|
this.store.undo();
|
|
61529
61616
|
}
|
|
61530
61617
|
clearAll() {
|
|
@@ -61537,6 +61624,7 @@ void main() {
|
|
|
61537
61624
|
});
|
|
61538
61625
|
this.managed.clear();
|
|
61539
61626
|
this.selectedId = null;
|
|
61627
|
+
this.clearActiveGeo();
|
|
61540
61628
|
}
|
|
61541
61629
|
deleteAnnotation(id) {
|
|
61542
61630
|
if (this.selectedId === id)
|
|
@@ -61617,10 +61705,153 @@ void main() {
|
|
|
61617
61705
|
hit(e) {
|
|
61618
61706
|
return raycastSurface(this.o.camera, this.o.container, this.o.mesh, e.clientX, e.clientY);
|
|
61619
61707
|
}
|
|
61620
|
-
/**
|
|
61708
|
+
/**
|
|
61709
|
+
* 事件目标是否是容器内的 WebGL canvas。
|
|
61710
|
+
* 仅认 canvas:面板(GUIDE / 控制面板 / 标注列表的 ✕ 按钮)都是 container 的子元素,
|
|
61711
|
+
* 若只判断 contains 会把面板上的点击也当成在模型上作画 —— 这会"穿透"删除按钮、
|
|
61712
|
+
* 让 ✕ 难以点中。只响应 canvas 上的指针事件即可彻底隔离 UI 与画布。
|
|
61713
|
+
*/
|
|
61621
61714
|
insideContainer(e) {
|
|
61622
61715
|
const t = e.target;
|
|
61623
|
-
return !!t && this.o.container.contains(t);
|
|
61716
|
+
return !!t && t.tagName === "CANVAS" && this.o.container.contains(t);
|
|
61717
|
+
}
|
|
61718
|
+
/** 射线拾取进行中的锚点小球,返回锚点下标;未命中返回 -1。 */
|
|
61719
|
+
pickGeoMarker(e) {
|
|
61720
|
+
if (!this.activeGeoMarkers.length)
|
|
61721
|
+
return -1;
|
|
61722
|
+
const rect = this.o.container.getBoundingClientRect();
|
|
61723
|
+
this.geoNdc.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
|
61724
|
+
this.geoNdc.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
61725
|
+
this.geoRay.setFromCamera(this.geoNdc, this.o.camera);
|
|
61726
|
+
const hits = this.geoRay.intersectObjects(this.activeGeoMarkers, false);
|
|
61727
|
+
if (!hits.length)
|
|
61728
|
+
return -1;
|
|
61729
|
+
return this.activeGeoMarkers.indexOf(hits[0].object);
|
|
61730
|
+
}
|
|
61731
|
+
/** 设置当前悬停的锚点(-1 = 无):更新高亮缩放、光标与"✕(可取消)"悬浮标。 */
|
|
61732
|
+
setGeoHover(idx) {
|
|
61733
|
+
if (idx === this.hoveredGeoMarker) {
|
|
61734
|
+
if (idx >= 0)
|
|
61735
|
+
this.positionGeoBadge(this.activeGeoMarkers[idx]);
|
|
61736
|
+
return;
|
|
61737
|
+
}
|
|
61738
|
+
const prev = this.activeGeoMarkers[this.hoveredGeoMarker];
|
|
61739
|
+
if (prev)
|
|
61740
|
+
prev.scale.setScalar(1);
|
|
61741
|
+
this.hoveredGeoMarker = idx;
|
|
61742
|
+
const cur = this.activeGeoMarkers[idx];
|
|
61743
|
+
if (cur) {
|
|
61744
|
+
cur.scale.setScalar(1.3);
|
|
61745
|
+
this.ensureGeoBadge().style.display = "flex";
|
|
61746
|
+
this.positionGeoBadge(cur);
|
|
61747
|
+
this.o.container.style.cursor = "pointer";
|
|
61748
|
+
}
|
|
61749
|
+
else {
|
|
61750
|
+
if (this.geoHoverBadge)
|
|
61751
|
+
this.geoHoverBadge.style.display = "none";
|
|
61752
|
+
this.o.container.style.cursor = "";
|
|
61753
|
+
}
|
|
61754
|
+
}
|
|
61755
|
+
/** 懒创建悬浮 ✕ 标(挂在 container 内,pointer-events:none 不挡点击)。 */
|
|
61756
|
+
ensureGeoBadge() {
|
|
61757
|
+
if (this.geoHoverBadge)
|
|
61758
|
+
return this.geoHoverBadge;
|
|
61759
|
+
const b = document.createElement("div");
|
|
61760
|
+
b.textContent = "✕";
|
|
61761
|
+
Object.assign(b.style, {
|
|
61762
|
+
position: "absolute",
|
|
61763
|
+
width: "16px",
|
|
61764
|
+
height: "16px",
|
|
61765
|
+
borderRadius: "50%",
|
|
61766
|
+
background: "#ff5d6c",
|
|
61767
|
+
color: "#fff",
|
|
61768
|
+
font: "700 10px/1 system-ui, sans-serif",
|
|
61769
|
+
display: "none",
|
|
61770
|
+
alignItems: "center",
|
|
61771
|
+
justifyContent: "center",
|
|
61772
|
+
pointerEvents: "none",
|
|
61773
|
+
zIndex: "15",
|
|
61774
|
+
boxShadow: "0 2px 6px rgba(0,0,0,.45)",
|
|
61775
|
+
transform: "translate(-50%, -50%)",
|
|
61776
|
+
left: "0px",
|
|
61777
|
+
top: "0px",
|
|
61778
|
+
});
|
|
61779
|
+
this.o.container.appendChild(b);
|
|
61780
|
+
this.geoHoverBadge = b;
|
|
61781
|
+
return b;
|
|
61782
|
+
}
|
|
61783
|
+
/** 把 ✕ 标定位到锚点投影到屏幕的位置(正中心,即光标悬停处,避免歧义)。 */
|
|
61784
|
+
positionGeoBadge(marker) {
|
|
61785
|
+
if (!marker)
|
|
61786
|
+
return;
|
|
61787
|
+
const b = this.ensureGeoBadge();
|
|
61788
|
+
this._projV.copy(marker.position).project(this.o.camera);
|
|
61789
|
+
const rect = this.o.container.getBoundingClientRect();
|
|
61790
|
+
const x = (this._projV.x * 0.5 + 0.5) * rect.width;
|
|
61791
|
+
const y = (-this._projV.y * 0.5 + 0.5) * rect.height;
|
|
61792
|
+
b.style.left = `${x}px`;
|
|
61793
|
+
b.style.top = `${y}px`;
|
|
61794
|
+
}
|
|
61795
|
+
/** 据当前锚点重建可见的锚点小球(锚点比放点稍大,便于看见与点中取消)。 */
|
|
61796
|
+
rebuildGeoMarkers() {
|
|
61797
|
+
this.setGeoHover(-1);
|
|
61798
|
+
for (const m of this.activeGeoMarkers) {
|
|
61799
|
+
this.o.scene.remove(m);
|
|
61800
|
+
this.disposeObject(m);
|
|
61801
|
+
}
|
|
61802
|
+
this.activeGeoMarkers = [];
|
|
61803
|
+
if (!this.activeGeo)
|
|
61804
|
+
return;
|
|
61805
|
+
for (const v of this.activeGeo.getAnchorLocals()) {
|
|
61806
|
+
const marker = makePointMarker(v, this.o.mesh, this.geodesicColor, this.markerRadius * 1.15);
|
|
61807
|
+
this.o.scene.add(marker);
|
|
61808
|
+
this.activeGeoMarkers.push(marker);
|
|
61809
|
+
}
|
|
61810
|
+
}
|
|
61811
|
+
/** 据当前锚点重画进行中的测地线(不闭合);不足两点时移除线。 */
|
|
61812
|
+
redrawGeoLine() {
|
|
61813
|
+
if (!this.activeGeo)
|
|
61814
|
+
return;
|
|
61815
|
+
const verts = this.activeGeo.buildVertices(false);
|
|
61816
|
+
if (verts.length < 2) {
|
|
61817
|
+
if (this.activeGeoLine) {
|
|
61818
|
+
this.o.scene.remove(this.activeGeoLine);
|
|
61819
|
+
this.disposeObject(this.activeGeoLine);
|
|
61820
|
+
this.activeGeoLine = undefined;
|
|
61821
|
+
}
|
|
61822
|
+
return;
|
|
61823
|
+
}
|
|
61824
|
+
if (!this.activeGeoLine) {
|
|
61825
|
+
this.activeGeoLine = makeContourLine(verts, this.geodesicColor, false, this.o.container, this.epsilon, this.o.mesh);
|
|
61826
|
+
this.o.scene.add(this.activeGeoLine);
|
|
61827
|
+
}
|
|
61828
|
+
else {
|
|
61829
|
+
updateContourLine(this.activeGeoLine, verts, false, this.epsilon, this.o.mesh);
|
|
61830
|
+
}
|
|
61831
|
+
}
|
|
61832
|
+
/** 丢弃进行中的测地线:移除并 dispose 线与全部锚点。 */
|
|
61833
|
+
clearActiveGeo() {
|
|
61834
|
+
this.setGeoHover(-1);
|
|
61835
|
+
if (this.activeGeoLine) {
|
|
61836
|
+
this.o.scene.remove(this.activeGeoLine);
|
|
61837
|
+
this.disposeObject(this.activeGeoLine);
|
|
61838
|
+
this.activeGeoLine = undefined;
|
|
61839
|
+
}
|
|
61840
|
+
for (const m of this.activeGeoMarkers) {
|
|
61841
|
+
this.o.scene.remove(m);
|
|
61842
|
+
this.disposeObject(m);
|
|
61843
|
+
}
|
|
61844
|
+
this.activeGeoMarkers = [];
|
|
61845
|
+
this.activeGeo = undefined;
|
|
61846
|
+
}
|
|
61847
|
+
/** 移除进行中测地线的全部锚点小球(落定后调用:只留下线)。 */
|
|
61848
|
+
removeGeoMarkers() {
|
|
61849
|
+
this.setGeoHover(-1);
|
|
61850
|
+
for (const m of this.activeGeoMarkers) {
|
|
61851
|
+
this.o.scene.remove(m);
|
|
61852
|
+
this.disposeObject(m);
|
|
61853
|
+
}
|
|
61854
|
+
this.activeGeoMarkers = [];
|
|
61624
61855
|
}
|
|
61625
61856
|
closeLastContour() {
|
|
61626
61857
|
const last = this.lastFreehand;
|
|
@@ -61663,7 +61894,10 @@ void main() {
|
|
|
61663
61894
|
}
|
|
61664
61895
|
else {
|
|
61665
61896
|
this.o.scene.remove(this.activeGeoLine);
|
|
61897
|
+
this.disposeObject(this.activeGeoLine);
|
|
61666
61898
|
}
|
|
61899
|
+
// 落定后清掉可见锚点,只留下贴合表面的线。
|
|
61900
|
+
this.removeGeoMarkers();
|
|
61667
61901
|
this.activeGeo = undefined;
|
|
61668
61902
|
this.activeGeoLine = undefined;
|
|
61669
61903
|
}
|
|
@@ -61675,6 +61909,10 @@ void main() {
|
|
|
61675
61909
|
return tag === "INPUT" || tag === "TEXTAREA" || t.isContentEditable;
|
|
61676
61910
|
}
|
|
61677
61911
|
dispose() {
|
|
61912
|
+
var _a;
|
|
61913
|
+
this.clearActiveGeo();
|
|
61914
|
+
(_a = this.geoHoverBadge) === null || _a === void 0 ? void 0 : _a.remove();
|
|
61915
|
+
this.geoHoverBadge = undefined;
|
|
61678
61916
|
window.removeEventListener("pointerdown", this.onPointerDown, true);
|
|
61679
61917
|
window.removeEventListener("pointermove", this.onPointerMove, true);
|
|
61680
61918
|
window.removeEventListener("pointerup", this.onPointerUp, true);
|
|
@@ -82662,7 +82900,7 @@ void main() {
|
|
|
82662
82900
|
}
|
|
82663
82901
|
|
|
82664
82902
|
// import * as kiwrious from "copper3d_plugin_heart_k";
|
|
82665
|
-
const REVISION = "v3.6.
|
|
82903
|
+
const REVISION = "v3.6.2-beta";
|
|
82666
82904
|
console.log(`%cCopper3D Visualisation %cBeta:${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
82667
82905
|
|
|
82668
82906
|
exports.AI_CHANNEL_HEX_COLORS = AI_CHANNEL_HEX_COLORS;
|
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 = "v3.6.
|
|
31
|
+
export declare const REVISION = "v3.6.2-beta";
|
|
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,7 +22,7 @@ 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
|
-
export const REVISION = "v3.6.
|
|
25
|
+
export const REVISION = "v3.6.2-beta";
|
|
26
26
|
console.log(`%cCopper3D Visualisation %cBeta:${REVISION}`, "padding: 3px;color:white; background:#023047", "padding: 3px;color:white; background:#f50a25");
|
|
27
27
|
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, };
|
|
28
28
|
//# sourceMappingURL=index.js.map
|
|
@@ -38,6 +38,7 @@ export declare class SurfaceAnnotator {
|
|
|
38
38
|
private readonly markerRadius;
|
|
39
39
|
private readonly epsilon;
|
|
40
40
|
private readonly minGap;
|
|
41
|
+
private readonly maxJump;
|
|
41
42
|
private graph;
|
|
42
43
|
private store;
|
|
43
44
|
private managed;
|
|
@@ -48,6 +49,12 @@ export declare class SurfaceAnnotator {
|
|
|
48
49
|
private lastFreehand?;
|
|
49
50
|
private activeGeo?;
|
|
50
51
|
private activeGeoLine?;
|
|
52
|
+
private activeGeoMarkers;
|
|
53
|
+
private geoRay;
|
|
54
|
+
private geoNdc;
|
|
55
|
+
private geoHoverBadge?;
|
|
56
|
+
private hoveredGeoMarker;
|
|
57
|
+
private _projV;
|
|
51
58
|
constructor(opts: SurfaceAnnotatorOptions);
|
|
52
59
|
/** 窗口尺寸变化时更新所有 fat line 的像素分辨率,否则线宽会失真。 */
|
|
53
60
|
private onResize;
|
|
@@ -86,9 +93,30 @@ export declare class SurfaceAnnotator {
|
|
|
86
93
|
private disposeObject;
|
|
87
94
|
private nextId;
|
|
88
95
|
private hit;
|
|
89
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* 事件目标是否是容器内的 WebGL canvas。
|
|
98
|
+
* 仅认 canvas:面板(GUIDE / 控制面板 / 标注列表的 ✕ 按钮)都是 container 的子元素,
|
|
99
|
+
* 若只判断 contains 会把面板上的点击也当成在模型上作画 —— 这会"穿透"删除按钮、
|
|
100
|
+
* 让 ✕ 难以点中。只响应 canvas 上的指针事件即可彻底隔离 UI 与画布。
|
|
101
|
+
*/
|
|
90
102
|
private insideContainer;
|
|
91
103
|
private onPointerDown;
|
|
104
|
+
/** 射线拾取进行中的锚点小球,返回锚点下标;未命中返回 -1。 */
|
|
105
|
+
private pickGeoMarker;
|
|
106
|
+
/** 设置当前悬停的锚点(-1 = 无):更新高亮缩放、光标与"✕(可取消)"悬浮标。 */
|
|
107
|
+
private setGeoHover;
|
|
108
|
+
/** 懒创建悬浮 ✕ 标(挂在 container 内,pointer-events:none 不挡点击)。 */
|
|
109
|
+
private ensureGeoBadge;
|
|
110
|
+
/** 把 ✕ 标定位到锚点投影到屏幕的位置(正中心,即光标悬停处,避免歧义)。 */
|
|
111
|
+
private positionGeoBadge;
|
|
112
|
+
/** 据当前锚点重建可见的锚点小球(锚点比放点稍大,便于看见与点中取消)。 */
|
|
113
|
+
private rebuildGeoMarkers;
|
|
114
|
+
/** 据当前锚点重画进行中的测地线(不闭合);不足两点时移除线。 */
|
|
115
|
+
private redrawGeoLine;
|
|
116
|
+
/** 丢弃进行中的测地线:移除并 dispose 线与全部锚点。 */
|
|
117
|
+
private clearActiveGeo;
|
|
118
|
+
/** 移除进行中测地线的全部锚点小球(落定后调用:只留下线)。 */
|
|
119
|
+
private removeGeoMarkers;
|
|
92
120
|
private onPointerMove;
|
|
93
121
|
private onPointerUp;
|
|
94
122
|
private closeLastContour;
|
|
@@ -10,9 +10,22 @@ export declare class GeodesicContour {
|
|
|
10
10
|
private mesh;
|
|
11
11
|
private anchors;
|
|
12
12
|
private segments;
|
|
13
|
+
private history;
|
|
13
14
|
constructor(graph: MeshGraph, mesh: THREE.Mesh);
|
|
15
|
+
/** 任何改动锚点前先存一份快照(顶点索引数组很小,快照成本可忽略)。 */
|
|
16
|
+
private snapshot;
|
|
14
17
|
/** 传入 local 命中点,snap 到最近顶点并对上一锚点求路径。 */
|
|
15
18
|
addAnchor(localHitPoint: THREE.Vector3): void;
|
|
19
|
+
/** 删除指定下标的锚点,重算相邻段(支持取消中间任意一点)。 */
|
|
20
|
+
removeAnchorAt(index: number): void;
|
|
21
|
+
/** 是否还有可撤销的编辑(加点/删点)。 */
|
|
22
|
+
canUndo(): boolean;
|
|
23
|
+
/** 撤销上一次锚点编辑(加点 → 删回去;删点 → 恢复)。无历史返回 false。 */
|
|
24
|
+
undoEdit(): boolean;
|
|
25
|
+
/** 据当前锚点序列整体重算所有相邻段路径。 */
|
|
26
|
+
private rebuildSegments;
|
|
27
|
+
/** 各锚点的 local 顶点(用于绘制可见的锚点标记)。 */
|
|
28
|
+
getAnchorLocals(): AnnotationVertex[];
|
|
16
29
|
get anchorCount(): number;
|
|
17
30
|
/** 把所有路径顶点(world)+ 法线 拼成折线;closed 时补一段首尾路径。 */
|
|
18
31
|
buildVertices(closed: boolean): AnnotationVertex[];
|
|
@@ -7,11 +7,18 @@ import type { AnnotationVertex, SurfaceHit } from "./types";
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class StrokeContour {
|
|
9
9
|
private minGap;
|
|
10
|
+
private maxJump;
|
|
10
11
|
private mesh;
|
|
11
12
|
private verts;
|
|
12
13
|
private last;
|
|
14
|
+
private lastNormal;
|
|
13
15
|
private has;
|
|
14
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @param minGap 采样最小间距(世界系),去抖
|
|
18
|
+
* @param maxJump 跳变阈值(世界系):距离 > maxJump 且法线大幅翻转时丢弃该样本,
|
|
19
|
+
* 避免笔触掠过沟缝/轮廓边时相邻点落在不同深度、直线段横穿模型。
|
|
20
|
+
*/
|
|
21
|
+
constructor(minGap: number, maxJump: number, mesh: THREE.Mesh);
|
|
15
22
|
begin(): void;
|
|
16
23
|
addSample(hit: SurfaceHit): void;
|
|
17
24
|
get vertices(): AnnotationVertex[];
|
package/dist/types/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 = "v3.6.
|
|
31
|
+
export declare const REVISION = "v3.6.2-beta";
|
|
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/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.2",
|
|
5
5
|
"main": "dist/bundle.umd.js",
|
|
6
6
|
"moudle": "dist/bundle.esm.js",
|
|
7
7
|
"types": "dist/types/index.d.ts",
|