diff --git a/src/kiri/mode/cam/work/prepare.js b/src/kiri/mode/cam/work/prepare.js index 3db3af20..c1dc7e9c 100644 --- a/src/kiri/mode/cam/work/prepare.js +++ b/src/kiri/mode/cam/work/prepare.js @@ -138,6 +138,7 @@ export async function prepare_one(widget, settings, print, firstPoint, update) { toolType, toolDiam, toolDiamMove, + toolDiamEpsilon, travelBounds, spindle = 0, spindleMax = device.spindleMax, @@ -211,6 +212,7 @@ export async function prepare_one(widget, settings, print, firstPoint, update) { toolType = tool.getType(); toolDiam = tool.fluteDiameter(); toolDiamMove = (tool.hasTaper() ? tolerance ?? toolDiam : toolDiam) * 2; + toolDiamEpsilon = toolDiam * 0.01, lastTool = toolID; } feedRate = Math.min(camFastFeed, feed || feedRate || plunge); @@ -593,9 +595,11 @@ export async function prepare_one(widget, settings, print, firstPoint, update) { if (lastTravelBounds) check.push(...lastTravelBounds); let from = toWidgetCoords(printPoint); let to = toWidgetCoords(point); + let ep = toolDiamEpsilon; for (let poly of check) { - let ints = poly.intersections(from, to); - if (ints.length) { + let ints = poly.intersections(from, to) ?? []; + let far = ints.filter(p => p.distTo2D(to) > ep && p.distTo2D(from) > ep); + if (far.length) { if (debug) console.log({ ints, poly, deltaXY, deltaZ }); upAndOver = "bounds"; break; diff --git a/src/void/api/solids.js b/src/void/api/solids.js index 56971f65..a7f221e3 100644 --- a/src/void/api/solids.js +++ b/src/void/api/solids.js @@ -1756,7 +1756,9 @@ function edgeKey(a, b) { const mesh = new THREE.Mesh(built.render, this._material.clone()); mesh.userData.solidId = id; mesh.userData.solid = true; - const edgesGeom = new THREE.EdgesGeometry(built.render, SOLID_CREASE_ANGLE_DEG); + // Derive visible hard edges from indexed manifold topology, not the + // creased render geometry, to avoid triangle/split seam artifacts. + const edgesGeom = new THREE.EdgesGeometry(built.indexed, SOLID_CREASE_ANGLE_DEG); const edges = new THREE.LineSegments(edgesGeom, this._edgeMaterial.clone()); edges.userData.solidId = id; edges.userData.solidEdge = true; @@ -1807,7 +1809,7 @@ function edgeKey(a, b) { const built = buildSolidGeometry(meshData); view.mesh.geometry = built.render; view.indexedGeometry = built.indexed; - view.edges.geometry = new THREE.EdgesGeometry(built.render, SOLID_CREASE_ANGLE_DEG); + view.edges.geometry = new THREE.EdgesGeometry(built.indexed, SOLID_CREASE_ANGLE_DEG); } // Build selectable face regions from the same geometry used for ray hits. // This keeps surface-region hover/selection aligned with rendered shading. diff --git a/src/void/solid/rebuild.js b/src/void/solid/rebuild.js index 4736ae3e..f0df1e1d 100644 --- a/src/void/solid/rebuild.js +++ b/src/void/solid/rebuild.js @@ -278,6 +278,8 @@ async function rebuildGeneratedSolidsFromSnapshot(snapshot, options = {}) { const operation = ['new', 'add', 'subtract'].includes(String(params.operation || 'new')) ? String(params.operation || 'new') : 'new'; + // Symmetric is a single extrusion whose local span is shifted to + // [-depth/2, +depth/2], not two opposing extrusions/union. const localZShift = symmetric ? (-depth / 2) : (direction === 'reverse' ? -depth : 0); const createdBodyIds = []; const bySketch = new Map();