diff --git a/src/components/ControlPanel.css b/src/components/ControlPanel.css
index 3a6f5ce..f420ccb 100644
--- a/src/components/ControlPanel.css
+++ b/src/components/ControlPanel.css
@@ -148,3 +148,34 @@
min-width: 100px;
text-align: right;
}
+
+.resolution-input,
+.allowance-input {
+ margin-bottom: 12px;
+}
+
+.resolution-input label,
+.allowance-input label {
+ color: #fff;
+ font-size: 13px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.resolution-input input,
+.allowance-input input {
+ width: 60px;
+ padding: 4px 8px;
+ border: 1px solid #444;
+ border-radius: 4px;
+ background: #1a1a2e;
+ color: #fff;
+ font-size: 13px;
+}
+
+.resolution-input input:focus,
+.allowance-input input:focus {
+ outline: none;
+ border-color: #e94560;
+}
diff --git a/src/components/ControlPanel.jsx b/src/components/ControlPanel.jsx
index 782f93c..21c8719 100644
--- a/src/components/ControlPanel.jsx
+++ b/src/components/ControlPanel.jsx
@@ -12,6 +12,7 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
const fileInputRef = useRef(null)
const workerRef = useRef(null)
const selectedToolRef = useRef('ball')
+ const upperEnvelopeRef = useRef(null)
const [isGenerating, setIsGenerating] = useState(false)
const [isRepairing, setIsRepairing] = useState(false)
const [repairProgress, setRepairProgress] = useState(0)
@@ -19,6 +20,8 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
const [selectedTool, setSelectedTool] = useState('ball')
const [computeMode, setComputeMode] = useState('worker')
const [webgpuAvailable, setWebgpuAvailable] = useState(false)
+ const [resolutionPercent, setResolutionPercent] = useState(12)
+ const [allowance, setAllowance] = useState(0.5)
useEffect(() => {
if (navigator.gpu) {
@@ -34,6 +37,10 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
selectedToolRef.current = selectedTool
}, [selectedTool])
+ useEffect(() => {
+ upperEnvelopeRef.current = upperEnvelope
+ }, [upperEnvelope])
+
useEffect(() => {
workerRef.current = new Worker(
new URL('../workers/repairWorker.js', import.meta.url),
@@ -51,8 +58,8 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
const toolpathPoints = []
const repairedPoints = []
const halfSize = MATERIAL_SIZE / 2
- const resolution = 0.1
- const gridSize = Math.ceil(MATERIAL_SIZE / resolution)
+ const resolution = upperEnvelopeRef.current.resolution
+ const gridSize = upperEnvelopeRef.current.gridSize
const gridOffset = halfSize
for (let gy = 0; gy < gridSize; gy++) {
@@ -199,35 +206,60 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
await new Promise(resolve => setTimeout(resolve, 50))
const geometry = model.geometry
- const resolution = 0.1
- const gridSize = Math.ceil(MATERIAL_SIZE / resolution)
- const depthMap = new Float32Array(gridSize * gridSize).fill(-Infinity)
-
- const vertices = geometry.attributes.position.array
- const bbox = geometry.boundingBox
+ let resolution
+ const tool = selectedToolRef.current
+ const toolDiameter = (tool === 'flat14') ? 6.35 : 3.175
+ if (tool === 'ball' || tool === 'flat18' || tool === 'flat14') {
+ resolution = toolDiameter * (resolutionPercent / 100)
+ } else {
+ resolution = 0.1
+ }
+
+ const gridSize = Math.ceil(MATERIAL_SIZE / resolution)
const halfSize = MATERIAL_SIZE / 2
const gridOffset = halfSize
+ const toGrid = (v) => Math.floor((v + gridOffset) / resolution)
- for (let i = 0; i < vertices.length; i += 3) {
- const x = vertices[i]
- const y = vertices[i + 1]
- const z = vertices[i + 2]
+ const vertices = geometry.attributes.position.array
+ const bbox = geometry.boundingBox
- const gridX = Math.floor((x + gridOffset) / resolution)
- const gridY = Math.floor((y + gridOffset) / resolution)
+ const depthMap = new Float32Array(gridSize * gridSize).fill(-Infinity)
- if (gridX >= 0 && gridX < gridSize && gridY >= 0 && gridY < gridSize) {
- const idx = gridY * gridSize + gridX
- if (z > depthMap[idx]) {
- depthMap[idx] = z
+ for (let i = 0; i < vertices.length; i += 9) {
+ const x0 = vertices[i], y0 = vertices[i + 1], z0 = vertices[i + 2]
+ const x1 = vertices[i + 3], y1 = vertices[i + 4], z1 = vertices[i + 5]
+ const x2 = vertices[i + 6], y2 = vertices[i + 7], z2 = vertices[i + 8]
+
+ const gMinX = Math.max(0, Math.min(toGrid(x0), toGrid(x1), toGrid(x2)))
+ const gMaxX = Math.min(gridSize - 1, Math.max(toGrid(x0), toGrid(x1), toGrid(x2)))
+ const gMinY = Math.max(0, Math.min(toGrid(y0), toGrid(y1), toGrid(y2)))
+ const gMaxY = Math.min(gridSize - 1, Math.max(toGrid(y0), toGrid(y1), toGrid(y2)))
+
+ const det = (y1 - y2) * (x0 - x2) + (x2 - x1) * (y0 - y2)
+ if (Math.abs(det) < 1e-10) continue
+
+ for (let gy = gMinY; gy <= gMaxY; gy++) {
+ for (let gx = gMinX; gx <= gMaxX; gx++) {
+ const px = gx * resolution - gridOffset + resolution / 2
+ const py = gy * resolution - gridOffset + resolution / 2
+
+ const lambda1 = ((y1 - y2) * (px - x2) + (x2 - x1) * (py - y2)) / det
+ const lambda2 = ((y2 - y0) * (px - x2) + (x0 - x2) * (py - y2)) / det
+ const lambda3 = 1 - lambda1 - lambda2
+
+ if (lambda1 >= 0 && lambda2 >= 0 && lambda3 >= 0) {
+ const z = lambda1 * z0 + lambda2 * z1 + lambda3 * z2
+ const idx = gy * gridSize + gx
+ if (z > depthMap[idx]) {
+ depthMap[idx] = z
+ }
+ }
}
}
}
- console.log('depthMap 采样完成, gridSize:', gridSize, '有效像素:', depthMap.filter(z => z !== -Infinity).length)
-
const surfacePoints = []
for (let gy = 0; gy < gridSize; gy++) {
for (let gx = 0; gx < gridSize; gx++) {
@@ -270,6 +302,14 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
toolRadius = 3.175 / 2
toolAngle = 0
tipRadius = 0
+ } else if (selectedTool === 'flat18') {
+ toolRadius = 3.175 / 2
+ toolAngle = 0
+ tipRadius = 0
+ } else if (selectedTool === 'flat14') {
+ toolRadius = 6.35 / 2
+ toolAngle = 0
+ tipRadius = 0
} else if (selectedTool === 'taper15') {
toolRadius = 3.175 / 2
toolAngle = 15
@@ -290,6 +330,7 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
toolRadius,
toolAngle,
tipRadius,
+ allowance,
(msg) => {
if (msg.type === 'progress') {
setRepairProgress(msg.progress)
@@ -336,7 +377,8 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
toolType: selectedTool,
toolRadius,
toolAngle,
- tipRadius
+ tipRadius,
+ allowance
})
}
}
@@ -367,6 +409,22 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
+ {(selectedTool === 'flat18' || selectedTool === 'flat14') && (
+
+
+
+ )}