加入采样精度、平底铣刀及平底铣刀余量参数

This commit is contained in:
bql
2026-05-27 00:52:39 +08:00
parent 36c82f953e
commit a2c4d441f3
5 changed files with 507 additions and 129 deletions

View File

@@ -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;
}

View File

@@ -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,34 +206,59 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
await new Promise(resolve => setTimeout(resolve, 50))
const geometry = model.geometry
const resolution = 0.1
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 depthMap = new Float32Array(gridSize * gridSize).fill(-Infinity)
const halfSize = MATERIAL_SIZE / 2
const gridOffset = halfSize
const toGrid = (v) => Math.floor((v + gridOffset) / resolution)
const vertices = geometry.attributes.position.array
const bbox = geometry.boundingBox
const halfSize = MATERIAL_SIZE / 2
const gridOffset = halfSize
const depthMap = new Float32Array(gridSize * gridSize).fill(-Infinity)
for (let i = 0; i < vertices.length; i += 3) {
const x = vertices[i]
const y = vertices[i + 1]
const z = vertices[i + 2]
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 gridX = Math.floor((x + gridOffset) / resolution)
const gridY = Math.floor((y + gridOffset) / resolution)
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)))
if (gridX >= 0 && gridX < gridSize && gridY >= 0 && gridY < gridSize) {
const idx = gridY * gridSize + gridX
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++) {
@@ -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
<div className="control-section">
<h3>2. 生成上包络面</h3>
<div className="resolution-input">
<label>
采样精度(刀具直径百分比):
<input
type="number"
min="1"
step="1"
value={resolutionPercent}
onChange={(e) => {
const val = parseInt(e.target.value)
if (val > 0) setResolutionPercent(val)
}}
/>
%
</label>
</div>
<button
className="btn btn-success"
onClick={generateUpperEnvelope}
@@ -405,6 +463,26 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
/>
1/8" 球刀
</label>
<label>
<input
type="radio"
name="tool"
value="flat18"
checked={selectedTool === 'flat18'}
onChange={() => setSelectedTool('flat18')}
/>
1/8" 平底铣刀
</label>
<label>
<input
type="radio"
name="tool"
value="flat14"
checked={selectedTool === 'flat14'}
onChange={() => setSelectedTool('flat14')}
/>
1/4" 平底铣刀
</label>
<label>
<input
type="radio"
@@ -426,6 +504,23 @@ function ControlPanel({ model, upperEnvelope, onModelImport, onGenerateUpper, on
1/8" 30° 尖刀 (0.1mm)
</label>
</div>
{(selectedTool === 'flat18' || selectedTool === 'flat14') && (
<div className="allowance-input">
<label>
余量 (mm):
<input
type="number"
min="0"
step="0.1"
value={allowance}
onChange={(e) => {
const val = parseFloat(e.target.value)
if (val >= 0) setAllowance(val)
}}
/>
</label>
</div>
)}
<button
className="btn btn-warning"
onClick={repairAndGenerate}

View File

@@ -214,7 +214,7 @@ function EnvelopeViewer({
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3))
const material = new THREE.PointsMaterial({
size: resolution * 0.8,
size: Math.max(resolution * 0.8, 0.15),
vertexColors: true,
transparent: true,
opacity: 0.8,

View File

@@ -135,7 +135,7 @@ fn repairedMain(@builtin(global_invocation_id) global_id: vec3<u32>) {
}
`
export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolType, toolRadius, toolAngle, tipRadius, onProgress) {
export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolType, toolRadius, toolAngle, tipRadius, allowance, onProgress) {
if (!navigator.gpu) {
throw new Error('WebGPU not supported')
}
@@ -152,6 +152,10 @@ export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolT
gpuInputMap[i] = depthMap[i] === -Infinity ? EMPTY_VALUE : depthMap[i]
}
const isFlatTool = toolType === 'flat18' || toolType === 'flat14'
const allowanceRadius = isFlatTool ? (toolRadius / 2 + allowance) : 0
const allowanceKernelRadius = isFlatTool ? Math.ceil(allowanceRadius / resolution) : 0
const kernelRadius = Math.ceil(toolRadius / resolution)
const computeToolProfile = (dist) => {
@@ -160,6 +164,8 @@ export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolT
return Math.sqrt(toolRadius * toolRadius - dist * dist)
}
return 0
} else if (toolType === 'flat18' || toolType === 'flat14') {
return 0
} else {
const halfAngle = toolAngle * Math.PI / 180 / 2
const tanHalf = Math.tan(halfAngle)
@@ -186,6 +192,20 @@ export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolT
const centerProfileZ = kernel.find(k => k.kx === 0 && k.ky === 0)?.profileZ || 0
const allowanceKernel = isFlatTool ? [] : null
if (isFlatTool) {
for (let ky = -allowanceKernelRadius; ky <= allowanceKernelRadius; ky++) {
for (let kx = -allowanceKernelRadius; kx <= allowanceKernelRadius; kx++) {
const dx = kx * resolution
const dy = ky * resolution
const dist = Math.sqrt(dx * dx + dy * dy)
if (dist <= allowanceRadius) {
allowanceKernel.push({ kx, ky })
}
}
}
}
const kernelData = new ArrayBuffer(kernel.length * 16)
const kernelView = new DataView(kernelData)
kernel.forEach((k, i) => {
@@ -278,12 +298,68 @@ export async function runWebGPUConvolution(depthMap, gridSize, resolution, toolT
const convolutionMap = await runPass('convolutionMain', inputBuffer, outputBuffer)
if (onProgress) onProgress({ type: 'progress', progress: 35, iteration: 1 })
let toolpathMap
if (isFlatTool) {
toolpathMap = new Float32Array(convolutionMap.length)
for (let gy = 0; gy < gridSize; gy++) {
for (let gx = 0; gx < gridSize; gx++) {
const centerIdx = gy * gridSize + gx
const currentZ = convolutionMap[centerIdx]
if (currentZ < -49.9) {
toolpathMap[centerIdx] = EMPTY_VALUE
continue
}
let maxZ = currentZ
for (const { kx, ky } of allowanceKernel) {
const nx = gx + kx
const ny = gy + ky
if (nx < 0 || nx >= gridSize || ny < 0 || ny >= gridSize) continue
const neighborIdx = ny * gridSize + nx
const neighborZ = convolutionMap[neighborIdx]
if (neighborZ > maxZ) {
maxZ = neighborZ
}
}
toolpathMap[centerIdx] = maxZ
}
}
} else {
device.queue.writeBuffer(inputBuffer, 0, convolutionMap)
const toolpathMap = await runPass('toolpathMain', inputBuffer, outputBuffer)
toolpathMap = await runPass('toolpathMain', inputBuffer, outputBuffer)
}
if (onProgress) onProgress({ type: 'progress', progress: 50, iteration: 1 })
let repairedMap
if (isFlatTool) {
repairedMap = new Float32Array(toolpathMap.length)
for (let gy = 0; gy < gridSize; gy++) {
for (let gx = 0; gx < gridSize; gx++) {
const centerIdx = gy * gridSize + gx
const toolpathZ = toolpathMap[centerIdx]
if (toolpathZ < -49.9) {
repairedMap[centerIdx] = EMPTY_VALUE
continue
}
let minZ = toolpathZ
for (const { kx, ky, profileZ } of kernel) {
const nx = gx + kx
const ny = gy + ky
if (nx < 0 || nx >= gridSize || ny < 0 || ny >= gridSize) continue
const neighborIdx = ny * gridSize + nx
const neighborToolpathZ = toolpathMap[neighborIdx]
if (neighborToolpathZ < -49.9) continue
const surfaceZ = neighborToolpathZ - profileZ
if (surfaceZ < minZ) {
minZ = surfaceZ
}
}
repairedMap[centerIdx] = minZ
}
}
} else {
device.queue.writeBuffer(inputBuffer, 0, convolutionMap)
const repairedMap = await runPass('repairedMain', inputBuffer, outputBuffer)
repairedMap = await runPass('repairedMain', inputBuffer, outputBuffer)
}
if (onProgress) onProgress({ type: 'progress', progress: 95, iteration: 1 })
inputBuffer.destroy()

View File

@@ -1,15 +1,13 @@
const MAX_CHUNK_SIZE = 2000
self.onmessage = function(e) {
const { depthMap, gridSize, resolution, toolType, toolRadius, toolAngle, tipRadius } = e.data
const { depthMap, gridSize, resolution, toolType, toolRadius, toolAngle, tipRadius, allowance } = e.data
const inputMap = new Float32Array(depthMap)
const convolutionMap = new Float32Array(depthMap.length)
convolutionMap.fill(-Infinity)
const repairedMap = new Float32Array(depthMap.length)
repairedMap.fill(-Infinity)
const isFlatTool = toolType === 'flat18' || toolType === 'flat14'
const allowanceRadius = isFlatTool ? (toolRadius / 2 + allowance) : 0
const allowanceKernelRadius = isFlatTool ? Math.ceil(allowanceRadius / resolution) : 0
const kernelRadius = toolType === 'ball'
? Math.ceil(toolRadius / resolution)
: Math.ceil(toolRadius / resolution)
const kernelRadius = Math.ceil(toolRadius / resolution)
const computeToolProfile = (dist) => {
if (toolType === 'ball') {
@@ -17,6 +15,8 @@ self.onmessage = function(e) {
return Math.sqrt(toolRadius * toolRadius - dist * dist)
}
return 0
} else if (toolType === 'flat18' || toolType === 'flat14') {
return 0
} else {
const halfAngle = toolAngle * Math.PI / 180 / 2
const tanHalf = Math.tan(halfAngle)
@@ -45,19 +45,94 @@ self.onmessage = function(e) {
}
const kernel = precomputeKernel()
const centerProfileZ = kernel.find(k => k.kx === 0 && k.ky === 0)?.profileZ || 0
let processedCount = 0
const totalPixels = gridSize * gridSize
const precomputeAllowanceKernel = () => {
if (!isFlatTool) return null
const allowanceKernel = []
for (let ky = -allowanceKernelRadius; ky <= allowanceKernelRadius; ky++) {
for (let kx = -allowanceKernelRadius; kx <= allowanceKernelRadius; kx++) {
const dx = kx * resolution
const dy = ky * resolution
const dist = Math.sqrt(dx * dx + dy * dy)
if (dist <= allowanceRadius) {
allowanceKernel.push({ kx, ky })
}
}
}
return allowanceKernel
}
for (let gy = 0; gy < gridSize; gy++) {
for (let gx = 0; gx < gridSize; gx++) {
const centerIdx = gy * gridSize + gx
const allowanceKernel = precomputeAllowanceKernel()
const runAllowanceConvolution = (inputMap, gs) => {
if (!isFlatTool) return inputMap
const outputMap = new Float32Array(gs * gs)
outputMap.fill(-Infinity)
for (let gy = 0; gy < gs; gy++) {
for (let gx = 0; gx < gs; gx++) {
const centerIdx = gy * gs + gx
const currentZ = inputMap[centerIdx]
if (currentZ === -Infinity) continue
let maxZ = currentZ
for (const { kx, ky } of allowanceKernel) {
const nx = gx + kx
const ny = gy + ky
if (nx < 0 || nx >= gs || ny < 0 || ny >= gs) continue
const neighborIdx = ny * gs + nx
const neighborZ = inputMap[neighborIdx]
if (neighborZ > maxZ) {
maxZ = neighborZ
}
}
outputMap[centerIdx] = maxZ
}
}
return outputMap
}
const runFlatToolpathRepaired = (toolpathMap, gs) => {
if (!isFlatTool) return toolpathMap
const outputMap = new Float32Array(gs * gs)
outputMap.fill(-Infinity)
for (let gy = 0; gy < gs; gy++) {
for (let gx = 0; gx < gs; gx++) {
const centerIdx = gy * gs + gx
const toolpathZ = toolpathMap[centerIdx]
if (toolpathZ === -Infinity) continue
let minZ = toolpathZ
for (const { kx, ky, profileZ } of kernel) {
const nx = gx + kx
const ny = gy + ky
if (nx < 0 || nx >= gs || ny < 0 || ny >= gs) continue
const neighborIdx = ny * gs + nx
const neighborToolpathZ = toolpathMap[neighborIdx]
if (neighborToolpathZ === -Infinity) continue
const surfaceZ = neighborToolpathZ - profileZ
if (surfaceZ < minZ) {
minZ = surfaceZ
}
}
outputMap[centerIdx] = minZ
}
}
return outputMap
}
const runConvolution = (inputMap, gs) => {
const outputMap = new Float32Array(gs * gs)
outputMap.fill(-Infinity)
for (let gy = 0; gy < gs; gy++) {
for (let gx = 0; gx < gs; gx++) {
const centerIdx = gy * gs + gx
const currentZ = inputMap[centerIdx]
if (currentZ === -Infinity) {
processedCount++
continue
}
if (currentZ === -Infinity) continue
let convolutionZ = currentZ
@@ -65,9 +140,9 @@ self.onmessage = function(e) {
const nx = gx + kx
const ny = gy + ky
if (nx < 0 || nx >= gridSize || ny < 0 || ny >= gridSize) continue
if (nx < 0 || nx >= gs || ny < 0 || ny >= gs) continue
const neighborIdx = ny * gridSize + nx
const neighborIdx = ny * gs + nx
const neighborZ = inputMap[neighborIdx]
if (neighborZ === -Infinity) continue
@@ -77,41 +152,23 @@ self.onmessage = function(e) {
}
}
convolutionMap[centerIdx] = convolutionZ
processedCount++
if (gy % 10 === 0) {
const progress = (processedCount / totalPixels) * 33
self.postMessage({
type: 'progress',
progress: Math.min(progress, 32.9),
iteration: 1
})
}
}
}
const centerProfileZ = kernel.find(k => k.kx === 0 && k.ky === 0)?.profileZ || 0
const toolpathMap = new Float32Array(depthMap.length)
for (let i = 0; i < depthMap.length; i++) {
if (convolutionMap[i] === -Infinity) {
toolpathMap[i] = -Infinity
} else {
toolpathMap[i] = convolutionMap[i] - centerProfileZ
outputMap[centerIdx] = convolutionZ
}
}
processedCount = 0
return outputMap
}
for (let gy = 0; gy < gridSize; gy++) {
for (let gx = 0; gx < gridSize; gx++) {
const centerIdx = gy * gridSize + gx
const runRepaired = (convolutionMap, gs) => {
const outputMap = new Float32Array(gs * gs)
outputMap.fill(-Infinity)
for (let gy = 0; gy < gs; gy++) {
for (let gx = 0; gx < gs; gx++) {
const centerIdx = gy * gs + gx
const convolutionZ = convolutionMap[centerIdx]
if (convolutionZ === -Infinity) {
processedCount++
continue
}
if (convolutionZ === -Infinity) continue
let repairedZ = convolutionZ
@@ -119,9 +176,9 @@ self.onmessage = function(e) {
const nx = gx + kx
const ny = gy + ky
if (nx < 0 || nx >= gridSize || ny < 0 || ny >= gridSize) continue
if (nx < 0 || nx >= gs || ny < 0 || ny >= gs) continue
const neighborIdx = ny * gridSize + nx
const neighborIdx = ny * gs + nx
const neighborConvolutionZ = convolutionMap[neighborIdx]
if (neighborConvolutionZ === -Infinity) continue
@@ -131,19 +188,55 @@ self.onmessage = function(e) {
}
}
repairedMap[centerIdx] = repairedZ
processedCount++
outputMap[centerIdx] = repairedZ
}
}
if (gy % 10 === 0) {
const progress = 50 + (processedCount / totalPixels) * 50
return outputMap
}
const runToolpath = (convolutionMap, gs) => {
const outputMap = new Float32Array(gs * gs)
for (let i = 0; i < gs * gs; i++) {
if (convolutionMap[i] === -Infinity) {
outputMap[i] = -Infinity
} else {
outputMap[i] = convolutionMap[i] - centerProfileZ
}
}
return outputMap
}
const inputMap = new Float32Array(depthMap)
const totalPixels = gridSize * gridSize
let processedCount = 0
if (gridSize <= MAX_CHUNK_SIZE) {
const convolutionMap = runConvolution(inputMap, gridSize)
processedCount = totalPixels
self.postMessage({
type: 'progress',
progress: Math.min(progress, 99.9),
progress: 33,
iteration: 1
})
const toolpathMap = isFlatTool
? runAllowanceConvolution(convolutionMap, gridSize)
: runToolpath(convolutionMap, gridSize)
self.postMessage({
type: 'progress',
progress: 50,
iteration: 1
})
const repairedMap = isFlatTool
? runFlatToolpathRepaired(toolpathMap, gridSize)
: runRepaired(convolutionMap, gridSize)
self.postMessage({
type: 'progress',
progress: 99.9,
iteration: 1
})
}
}
}
self.postMessage({
type: 'complete',
@@ -151,5 +244,88 @@ self.onmessage = function(e) {
toolpathMap,
repairedMap,
iterations: 1
}, [convolutionMap.buffer, toolpathMap.buffer, repairedMap.buffer])
} else {
const numChunks = 2
const chunkSize = Math.ceil(gridSize / numChunks)
const overlap = kernelRadius
const convolutionMap = new Float32Array(totalPixels)
convolutionMap.fill(-Infinity)
for (let cy = 0; cy < numChunks; cy++) {
for (let cx = 0; cx < numChunks; cx++) {
const startY = cy * chunkSize
const startX = cx * chunkSize
const endY = Math.min(startY + chunkSize, gridSize)
const endX = Math.min(startX + chunkSize, gridSize)
const chunkH = endY - startY
const chunkW = endX - startX
const chunkSizeWithOverlap = (chunkH + overlap * 2) * (chunkW + overlap * 2)
const chunkInput = new Float32Array(chunkSizeWithOverlap)
chunkInput.fill(-Infinity)
for (let oy = -overlap; oy < chunkH + overlap; oy++) {
for (let ox = -overlap; ox < chunkW + overlap; ox++) {
const gy = startY + oy
const gx = startX + ox
if (gy < 0 || gy >= gridSize || gx < 0 || gx >= gridSize) continue
const chunkIdx = (oy + overlap) * (chunkW + overlap * 2) + (ox + overlap)
const globalIdx = gy * gridSize + gx
chunkInput[chunkIdx] = inputMap[globalIdx]
}
}
const chunkConvolution = runConvolution(chunkInput, chunkW + overlap * 2)
for (let oy = 0; oy < chunkH; oy++) {
for (let ox = 0; ox < chunkW; ox++) {
const chunkIdx = (oy + overlap) * (chunkW + overlap * 2) + (ox + overlap)
const globalIdx = (startY + oy) * gridSize + (startX + ox)
if (chunkConvolution[chunkIdx] > convolutionMap[globalIdx]) {
convolutionMap[globalIdx] = chunkConvolution[chunkIdx]
}
}
}
processedCount += chunkH * chunkW
const progress = (processedCount / totalPixels) * 33
self.postMessage({
type: 'progress',
progress: Math.min(progress, 32.9),
iteration: 1
})
}
}
self.postMessage({
type: 'progress',
progress: 33,
iteration: 1
})
const toolpathMap = isFlatTool
? runAllowanceConvolution(convolutionMap, gridSize)
: new Float32Array(totalPixels).map((_, i) => convolutionMap[i] === -Infinity ? -Infinity : convolutionMap[i] - centerProfileZ)
self.postMessage({
type: 'progress',
progress: 50,
iteration: 1
})
const repairedMap = isFlatTool
? runFlatToolpathRepaired(toolpathMap, gridSize)
: runRepaired(convolutionMap, gridSize)
self.postMessage({
type: 'complete',
convolutionMap,
toolpathMap,
repairedMap,
iterations: 1
}, [convolutionMap.buffer, toolpathMap.buffer, repairedMap.buffer])
}
}