@yumiai/chat-widget 0.1.2 → 0.2.1

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.
Files changed (107) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +119 -22
  3. package/dist/ExcelCore-DJOIVQMI.js +11 -0
  4. package/dist/ExcelCore-DJOIVQMI.js.map +1 -0
  5. package/dist/ExcelViewer-3YLLYYIQ.js +65 -0
  6. package/dist/ExcelViewer-3YLLYYIQ.js.map +1 -0
  7. package/dist/GerberViewerA2UI-7CNT7HX4.css +693 -0
  8. package/dist/GerberViewerA2UI-7CNT7HX4.css.map +1 -0
  9. package/dist/GerberViewerA2UI-X5FWAD5M.js +57 -0
  10. package/dist/GerberViewerA2UI-X5FWAD5M.js.map +1 -0
  11. package/dist/GraphStatsLegend-D5bPeXB_.d.cts +607 -0
  12. package/dist/GraphStatsLegend-D5bPeXB_.d.ts +607 -0
  13. package/dist/JsonRenderStandalone-EIZM62JU.js +18 -0
  14. package/dist/JsonRenderStandalone-EIZM62JU.js.map +1 -0
  15. package/dist/JsonRenderStandalone-POB4Q3N3.css +2384 -0
  16. package/dist/JsonRenderStandalone-POB4Q3N3.css.map +1 -0
  17. package/dist/JsonRenderStandalone-UsTcST4G.d.cts +23 -0
  18. package/dist/JsonRenderStandalone-UsTcST4G.d.ts +23 -0
  19. package/dist/KicadViewer-GV6ZC4AQ.js +124 -0
  20. package/dist/KicadViewer-GV6ZC4AQ.js.map +1 -0
  21. package/dist/KicadViewerCore-U7BWZHKJ.js +11 -0
  22. package/dist/KicadViewerCore-U7BWZHKJ.js.map +1 -0
  23. package/dist/PdfViewer-CHPDRK46.js +51 -0
  24. package/dist/PdfViewer-CHPDRK46.js.map +1 -0
  25. package/dist/PdfViewer-LPYGQETK.css +1899 -0
  26. package/dist/PdfViewer-LPYGQETK.css.map +1 -0
  27. package/dist/PdfViewerCore-HJPEHSRA.js +364 -0
  28. package/dist/PdfViewerCore-HJPEHSRA.js.map +1 -0
  29. package/dist/PowerPointCore-FPDR2BL4.js +11 -0
  30. package/dist/PowerPointCore-FPDR2BL4.js.map +1 -0
  31. package/dist/PowerPointViewer-LQTO6UCU.js +61 -0
  32. package/dist/PowerPointViewer-LQTO6UCU.js.map +1 -0
  33. package/dist/StepViewerCore-7W3L3R4E.js +285 -0
  34. package/dist/StepViewerCore-7W3L3R4E.js.map +1 -0
  35. package/dist/ThreeViewerCore-N3QJD5QI.js +161 -0
  36. package/dist/ThreeViewerCore-N3QJD5QI.js.map +1 -0
  37. package/dist/WordCore-JKSXK2XD.js +11 -0
  38. package/dist/WordCore-JKSXK2XD.js.map +1 -0
  39. package/dist/WordViewer-ZHCQMHOH.js +61 -0
  40. package/dist/WordViewer-ZHCQMHOH.js.map +1 -0
  41. package/dist/chunk-2SKA3F5U.js +88 -0
  42. package/dist/chunk-2SKA3F5U.js.map +1 -0
  43. package/dist/chunk-2UC7YLVX.js +318 -0
  44. package/dist/chunk-2UC7YLVX.js.map +1 -0
  45. package/dist/chunk-3R6T3LBR.js +24 -0
  46. package/dist/chunk-3R6T3LBR.js.map +1 -0
  47. package/dist/chunk-56WRZM3R.js +398 -0
  48. package/dist/chunk-56WRZM3R.js.map +1 -0
  49. package/dist/chunk-7A4FY6FK.js +10226 -0
  50. package/dist/chunk-7A4FY6FK.js.map +1 -0
  51. package/dist/chunk-7D4SUZUM.js +38 -0
  52. package/dist/chunk-7D4SUZUM.js.map +1 -0
  53. package/dist/chunk-7S67DOHQ.js +436 -0
  54. package/dist/chunk-7S67DOHQ.js.map +1 -0
  55. package/dist/chunk-CFKGNAJM.js +14013 -0
  56. package/dist/chunk-CFKGNAJM.js.map +1 -0
  57. package/dist/chunk-GAMA3VA7.js +99 -0
  58. package/dist/chunk-GAMA3VA7.js.map +1 -0
  59. package/dist/chunk-GYXTSY22.js +639 -0
  60. package/dist/chunk-GYXTSY22.js.map +1 -0
  61. package/dist/chunk-K4KGNVL5.js +77 -0
  62. package/dist/chunk-K4KGNVL5.js.map +1 -0
  63. package/dist/chunk-KQV7IKET.js +1621 -0
  64. package/dist/chunk-KQV7IKET.js.map +1 -0
  65. package/dist/chunk-O3NXUM6C.js +1871 -0
  66. package/dist/chunk-O3NXUM6C.js.map +1 -0
  67. package/dist/chunk-PZXSASDY.js +83 -0
  68. package/dist/chunk-PZXSASDY.js.map +1 -0
  69. package/dist/chunk-QLVPIM6R.js +595 -0
  70. package/dist/chunk-QLVPIM6R.js.map +1 -0
  71. package/dist/chunk-VXJWGLZ7.js +21 -0
  72. package/dist/chunk-VXJWGLZ7.js.map +1 -0
  73. package/dist/chunk-XQ562W7I.js +116 -0
  74. package/dist/chunk-XQ562W7I.js.map +1 -0
  75. package/dist/components/JsonRender/standalone.cjs +39368 -0
  76. package/dist/components/JsonRender/standalone.cjs.map +1 -0
  77. package/dist/components/JsonRender/standalone.css +2384 -0
  78. package/dist/components/JsonRender/standalone.css.map +1 -0
  79. package/dist/components/JsonRender/standalone.d.cts +16 -0
  80. package/dist/components/JsonRender/standalone.d.ts +16 -0
  81. package/dist/components/JsonRender/standalone.js +38 -0
  82. package/dist/components/JsonRender/standalone.js.map +1 -0
  83. package/dist/gerber-2d-entry-OQ4SQRBY.js +3950 -0
  84. package/dist/gerber-2d-entry-OQ4SQRBY.js.map +1 -0
  85. package/dist/gerber-3d-entry-DEHDBOO2.js +3679 -0
  86. package/dist/gerber-3d-entry-DEHDBOO2.js.map +1 -0
  87. package/dist/gerber-simulation-entry-EBDX72XE.js +1801 -0
  88. package/dist/gerber-simulation-entry-EBDX72XE.js.map +1 -0
  89. package/dist/index.cjs +60113 -2970
  90. package/dist/index.cjs.map +1 -1
  91. package/dist/index.css +11342 -1708
  92. package/dist/index.css.map +1 -1
  93. package/dist/index.d.cts +3275 -77
  94. package/dist/index.d.ts +3275 -77
  95. package/dist/index.js +18078 -2540
  96. package/dist/index.js.map +1 -1
  97. package/dist/provenance/index.cjs +2248 -0
  98. package/dist/provenance/index.cjs.map +1 -0
  99. package/dist/provenance/index.css +52 -0
  100. package/dist/provenance/index.css.map +1 -0
  101. package/dist/provenance/index.d.cts +12 -0
  102. package/dist/provenance/index.d.ts +12 -0
  103. package/dist/provenance/index.js +27 -0
  104. package/dist/provenance/index.js.map +1 -0
  105. package/dist/resolveToArrayBuffer-AQIDZHSQ.js +23 -0
  106. package/dist/resolveToArrayBuffer-AQIDZHSQ.js.map +1 -0
  107. package/package.json +98 -17
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/jetPaveGerberViewer/src/viewer-src/simulation/js/main.js"],"sourcesContent":["/**\n * PCB 仿真图渲染器\n * 模拟实际 PCB 制造后的外观\n */\n\nimport JSZip from 'jszip'\nimport { extractRar } from '../../common/archiveExtractor.js'\nimport { GerberParser } from '../../3dPage/js/gerber-parser.js'\nimport { parseUrlId, parseUrlFileUrl, fetchFileById, fetchFileByUrl } from '../../common/gerber-common.js'\n\n// ==================== 常量定义 ====================\n\n// 表面处理颜色映射\nconst FINISH_COLORS = {\n 'ENIG': '#D4AF37', // 沉金 - 金色\n 'HASL': '#C0C0C0', // 喷锡 - 银色\n 'OSP': '#B87333', // OSP - 铜色\n 'SILVER': '#E8E8E8', // 镀银 - 亮银色\n 'ENEPIG': '#E6C87A' // 化镍钯金 - 浅金色\n}\n\n// ==================== 全局状态 ====================\n\nlet layers = {} // 存储解析后的各层数据\nlet boardBounds = null // 板子边界\nlet refBounds = null // 参考边界(从铜层/阻焊层计算,用于选择正确的 GKO 轮廓)\nlet currentView = 'top' // 当前视图 (top/bottom)\nlet zoomLevel = 1\nlet panOffset = { x: 0, y: 0 }\nlet isDragging = false\nlet lastMousePos = { x: 0, y: 0 }\n\n// UI 元素\nlet canvas, ctx\nlet loadingOverlay, loadingText, statusLeft, statusRight\n\n// 配置选项\nlet config = {\n maskColor: '#1C590B',\n silkColor: '#ffffff',\n surfaceFinish: 'ENIG',\n showMask: true,\n showSilk: true,\n showDrill: true\n}\n\n// 单位转换比例\nlet unitScale = 1\n\n// 调试开关(生产环境设为 false)\nconst DEBUG = true\n\n// 图层缓存(offscreen canvas)\nlet layerCache = {\n top: null,\n bottom: null,\n dirty: true, // 是否需要重新渲染\n lastWidth: 0,\n lastHeight: 0\n}\n\n// 渲染防抖\nlet renderRAF = null\n\n// ==================== 初始化 ====================\n\nif (DEBUG) console.log('[simulation] main.js 已加载')\n\n// 初始化函数\nfunction initSimulation() {\n if (DEBUG) console.log('[simulation] 开始初始化')\n initUI()\n initEventListeners()\n initUrlHandler()\n}\n\n// 如果 DOM 已经就绪,立即初始化;否则等待 DOMContentLoaded\nif (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', initSimulation)\n} else {\n // DOM 已经就绪(在动态导入的情况下)\n initSimulation()\n}\n\nfunction initUI() {\n canvas = document.getElementById('simulationCanvas')\n ctx = canvas.getContext('2d')\n loadingOverlay = document.getElementById('loadingOverlay')\n loadingText = document.getElementById('loadingText')\n statusLeft = document.getElementById('statusLeft')\n statusRight = document.getElementById('statusRight')\n \n // 初始渲染空画布(纯黑背景)\n render()\n}\n\nfunction initEventListeners() {\n // 文件上传 - 直接使用按钮样式的 input\n const fileInput = document.getElementById('fileInput')\n \n fileInput.addEventListener('change', async (e) => {\n const files = Array.from(e.target.files)\n if (files.length > 0) {\n await handleFiles(files)\n }\n })\n \n // 视图切换\n document.getElementById('btnTopView').addEventListener('click', () => setView('top'))\n document.getElementById('btnBottomView').addEventListener('click', () => setView('bottom'))\n \n // 颜色选择\n document.getElementById('maskColorSelect').addEventListener('change', (e) => {\n config.maskColor = e.target.value\n document.getElementById('maskColorPreview').style.background = e.target.value\n invalidateCache()\n render()\n })\n document.getElementById('silkColorSelect').addEventListener('change', (e) => {\n config.silkColor = e.target.value\n document.getElementById('silkColorPreview').style.background = e.target.value\n invalidateCache()\n render()\n })\n document.getElementById('finishSelect').addEventListener('change', (e) => {\n config.surfaceFinish = e.target.value\n document.getElementById('finishColorPreview').style.background = FINISH_COLORS[e.target.value]\n invalidateCache()\n render()\n })\n \n // 显示控制\n document.getElementById('showMask').addEventListener('change', (e) => {\n config.showMask = e.target.checked\n invalidateCache()\n render()\n })\n document.getElementById('showSilk').addEventListener('change', (e) => {\n config.showSilk = e.target.checked\n invalidateCache()\n render()\n })\n document.getElementById('showDrill').addEventListener('change', (e) => {\n config.showDrill = e.target.checked\n invalidateCache()\n render()\n })\n \n // 导出和清空\n document.getElementById('btnExport').addEventListener('click', exportImage)\n document.getElementById('btnClear').addEventListener('click', clearAll)\n \n const canvasContainer = document.getElementById('canvasContainer')\n \n // 鼠标滚轮缩放(保持宽高比)\n canvasContainer.addEventListener('wheel', (e) => {\n e.preventDefault()\n if (!boardBounds) return\n \n const delta = e.deltaY > 0 ? 0.9 : 1.1\n zoomLevel *= delta\n \n // 限制缩放范围\n zoomLevel = Math.max(0.1, Math.min(10, zoomLevel))\n \n render()\n })\n \n // 鼠标拖动\n canvas.addEventListener('mousedown', (e) => {\n if (e.button === 0) { // 左键\n isDragging = true\n lastMousePos = { x: e.clientX, y: e.clientY }\n canvas.style.cursor = 'grabbing'\n }\n })\n \n window.addEventListener('mousemove', (e) => {\n if (isDragging) {\n const dx = e.clientX - lastMousePos.x\n const dy = e.clientY - lastMousePos.y\n panOffset.x += dx\n panOffset.y += dy\n lastMousePos = { x: e.clientX, y: e.clientY }\n render()\n }\n })\n \n window.addEventListener('mouseup', () => {\n if (isDragging) {\n isDragging = false\n canvas.style.cursor = 'grab'\n }\n })\n}\n\nfunction initUrlHandler() {\n // 如果在 render 页面中运行,跳过 URL 处理(render 页面会统一处理)\n if (window.isRenderPage) {\n if (DEBUG) console.log('[simulation] 在 render 页面中运行,跳过 URL 处理')\n return\n }\n \n // 检查 URL 中是否有 id 参数\n if (DEBUG) console.log('[simulation] initUrlHandler 被调用, URL:', window.location.href)\n const id = parseUrlId()\n const url = parseUrlFileUrl()\n if (DEBUG) console.log('[simulation] 解析到的 ID:', id, 'URL:', url)\n\n if (id) {\n if (DEBUG) console.log('[simulation] 开始从接口加载文件, ID:', id)\n loadFromId(id)\n return\n }\n\n if (url) {\n if (DEBUG) console.log('[simulation] 开始从链接加载文件:', url)\n loadFromUrl(url)\n }\n}\n\n// ==================== 文件处理 ====================\n\nasync function handleFiles(files) {\n console.log('[性能] handleFiles 开始')\n const startTimeTotal = performance.now()\n \n showLoading('正在解析文件...')\n \n try {\n console.log('[性能] 开始解压文件...')\n const startTimeExtract = performance.now()\n let gerberFiles = []\n \n for (const file of files) {\n const ext = file.name.split('.').pop().toLowerCase()\n \n if (ext === 'zip') {\n console.log(`[性能] 解压 ZIP: ${file.name}`)\n const startTimeZip = performance.now()\n const extracted = await extractZip(file)\n const endTimeZip = performance.now()\n console.log(`[性能] ZIP 解压完成,耗时: ${(endTimeZip - startTimeZip).toFixed(2)}ms, 文件数: ${extracted.length}`)\n gerberFiles.push(...extracted)\n } else if (ext === 'rar') {\n console.log(`[性能] 解压 RAR: ${file.name}`)\n const startTimeRar = performance.now()\n try {\n const extracted = await extractRar(file)\n const endTimeRar = performance.now()\n console.log(`[性能] RAR 解压完成,耗时: ${(endTimeRar - startTimeRar).toFixed(2)}ms, 文件数: ${extracted.length}`)\n gerberFiles.push(...extracted)\n } catch (rarError) {\n // 如果 RAR 实际是 ZIP 格式\n if (rarError.isActualZip) {\n const extracted = await extractZip(file)\n const endTimeRar = performance.now()\n console.log(`[性能] RAR(实为ZIP) 解压完成,耗时: ${(endTimeRar - startTimeRar).toFixed(2)}ms, 文件数: ${extracted.length}`)\n gerberFiles.push(...extracted)\n } else {\n throw rarError\n }\n }\n } else {\n gerberFiles.push(file)\n }\n }\n \n const endTimeExtract = performance.now()\n console.log(`[性能] 文件解压总耗时: ${(endTimeExtract - startTimeExtract).toFixed(2)}ms, 解压后文件数: ${gerberFiles.length}`)\n \n if (gerberFiles.length === 0) {\n throw new Error('未找到有效的 Gerber 文件')\n }\n \n console.log('[性能] 开始解析 Gerber 文件...')\n const startTimeParse = performance.now()\n await parseGerberFiles(gerberFiles)\n const endTimeParse = performance.now()\n console.log(`[性能] Gerber 文件解析完成,耗时: ${((endTimeParse - startTimeParse)/1000).toFixed(2)}秒`)\n \n console.log('[性能] 开始更新 UI 和渲染...')\n const startTimeRender = performance.now()\n invalidateCache()\n updateLayerList()\n fitToView()\n render()\n const endTimeRender = performance.now()\n console.log(`[性能] UI 更新和渲染完成,耗时: ${(endTimeRender - startTimeRender).toFixed(2)}ms`)\n \n const endTimeTotal = performance.now()\n const totalTime = (endTimeTotal - startTimeTotal) / 1000\n console.log(`[性能] ========== handleFiles 完成,总耗时: ${totalTime.toFixed(2)}秒 ==========`)\n console.log(`[性能] 详细耗时:解压=${((endTimeExtract-startTimeExtract)/1000).toFixed(2)}秒, 解析=${((endTimeParse-startTimeParse)/1000).toFixed(2)}秒, 渲染=${((endTimeRender-startTimeRender)/1000).toFixed(2)}秒`)\n \n hideLoading()\n updateStatus(`已加载 ${Object.keys(layers).length} 个图层`)\n \n } catch (error) {\n const endTimeTotal = performance.now()\n const totalTime = (endTimeTotal - startTimeTotal) / 1000\n console.error(`[错误] 处理文件失败 (耗时: ${totalTime.toFixed(2)}秒):`, error)\n hideLoading()\n updateStatus('错误: ' + error.message, true)\n }\n}\n\nasync function loadFromId(id) {\n if (DEBUG) console.log('[simulation] loadFromId 被调用, ID:', id)\n const startTimeTotal = performance.now()\n showLoading('正在从服务器加载...')\n \n try {\n // 使用公共函数 fetchFileById 获取文件(已包含性能监控和下载进度显示)\n if (DEBUG) console.log('[simulation] 使用 fetchFileById 获取文件...')\n await fetchFileById(\n id,\n async (files) => {\n // 文件获取成功,处理文件\n if (DEBUG) console.log('[simulation] 收到文件,开始处理...')\n await handleFiles(files)\n \n const finalTime = performance.now()\n const grandTotal = (finalTime - startTimeTotal) / 1000\n console.log(`[性能] ========== 从开始加载到处理完成总耗时: ${grandTotal.toFixed(2)}秒 ==========`)\n },\n (message, type) => {\n // 状态更新回调(输出到console以显示下载进度)\n console.log(`[simulation] 状态: ${message}`)\n }\n )\n } catch (error) {\n const endTime = performance.now()\n const errorTime = (endTime - startTimeTotal) / 1000\n console.error(`[simulation] 获取文件失败 (耗时: ${errorTime.toFixed(2)}秒):`, error)\n hideLoading()\n updateStatus(`获取文件失败: ${error.message}`, true)\n }\n}\n\nasync function loadFromUrl(url) {\n if (DEBUG) console.log('[simulation] loadFromUrl 被调用, URL:', url)\n const startTimeTotal = performance.now()\n showLoading('正在从链接加载...')\n\n try {\n if (DEBUG) console.log('[simulation] 使用 fetchFileByUrl 获取文件...')\n await fetchFileByUrl(\n url,\n async (files) => {\n if (DEBUG) console.log('[simulation] 收到文件,开始处理...')\n await handleFiles(files)\n\n const finalTime = performance.now()\n const grandTotal = (finalTime - startTimeTotal) / 1000\n console.log(`[性能] ========== 从开始加载到处理完成总耗时: ${grandTotal.toFixed(2)}秒 ==========`)\n },\n (message, type) => {\n console.log(`[simulation] 状态: ${message}`)\n }\n )\n } catch (error) {\n const endTime = performance.now()\n const errorTime = (endTime - startTimeTotal) / 1000\n console.error(`[simulation] 获取文件失败 (耗时: ${errorTime.toFixed(2)}秒):`, error)\n hideLoading()\n updateStatus(`获取文件失败: ${error.message}`, true)\n }\n}\n\nasync function extractZip(zipFile) {\n const zip = await JSZip.loadAsync(zipFile)\n const files = []\n \n for (const filename in zip.files) {\n if (zip.files[filename].dir) continue\n if (!isGerberFile(filename)) continue\n \n const content = await zip.files[filename].async('blob')\n files.push(new File([content], filename))\n }\n \n return files\n}\n\nfunction isGerberFile(fileName) {\n const validExtensions = [\n '.gtl', '.gbl', '.gts', '.gbs', '.gto', '.gbo',\n '.gtp', '.gbp', '.gm1', '.drl', '.gbr', '.gko',\n '.gdl', '.gdd', '.gm', '.gd1', '.gg1', '.gpb', '.gpt', '.txt',\n '.art', '.d', '.rou', '.pho'\n ]\n const ext = '.' + fileName.split('.').pop().toLowerCase()\n return validExtensions.includes(ext) || \n /^\\.g\\d+$/i.test(ext) || \n /^\\.gm\\d+$/i.test(ext) ||\n /^\\.gp\\d+$/i.test(ext)\n}\n\n// ==================== Gerber 解析 ====================\n\nasync function parseGerberFiles(files) {\n layers = {}\n boardBounds = null\n refBounds = null\n \n // 关键修复:调整文件解析顺序,先解析 Gerber 层再解析钻孔层\n // 这样钻孔文件可以继承 Gerber 的 %FS/%MO 格式信息,避免坐标缩放错误\n const parseQueue = []\n for (const file of files) {\n const layerType = getLayerType(file.name)\n if (!layerType) continue\n const isDrill = layerType === 'DRL' || layerType === 'DRL2'\n parseQueue.push({ file, layerType, isDrill })\n }\n // 非钻孔文件优先解析\n parseQueue.sort((a, b) => (a.isDrill === b.isDrill ? 0 : (a.isDrill ? 1 : -1)))\n if (DEBUG) console.log('[仿真图] 文件解析顺序:', parseQueue.map(x => x.file.name))\n \n for (const { file, layerType } of parseQueue) {\n try {\n showLoading(`正在解析 ${file.name}...`)\n \n // 使用 GerberParser 解析(与 3D 渲染器相同)\n const result = await GerberParser.parseFile(file, '#ffffff')\n \n if (!result || !result.data) {\n console.warn(`解析 ${file.name} 返回空数据`)\n continue\n }\n \n // 对于钻孔层(DRL/DRL2),如果已存在则合并数据\n if ((layerType === 'DRL' || layerType === 'DRL2') && layers[layerType]) {\n if (DEBUG) console.log(`[仿真图] 合并钻孔层 ${layerType}: ${file.name}`)\n const existing = layers[layerType]\n \n // 合并 vertices\n if (result.data.vertices && existing.data.vertices) {\n const newVertices = new Float32Array(existing.data.vertices.length + result.data.vertices.length)\n newVertices.set(existing.data.vertices, 0)\n newVertices.set(result.data.vertices, existing.data.vertices.length)\n existing.data.vertices = newVertices\n }\n \n // 合并 lineStrips,调整 start 偏移\n if (result.data.lineStrips && existing.data.lineStrips) {\n const vertexOffset = existing.data.vertices ? (existing.data.vertices.length - result.data.vertices.length) / 3 : 0\n const adjustedStrips = result.data.lineStrips.map(strip => ({\n start: strip.start + vertexOffset,\n count: strip.count\n }))\n existing.data.lineStrips = [...existing.data.lineStrips, ...adjustedStrips]\n }\n \n // 合并 bounds\n if (result.bounds && existing.bounds) {\n existing.bounds.minX = Math.min(existing.bounds.minX, result.bounds.minX)\n existing.bounds.minY = Math.min(existing.bounds.minY, result.bounds.minY)\n existing.bounds.maxX = Math.max(existing.bounds.maxX, result.bounds.maxX)\n existing.bounds.maxY = Math.max(existing.bounds.maxY, result.bounds.maxY)\n }\n \n existing.name += `, ${file.name.split(/[/\\\\]/).pop()}`\n } else {\n layers[layerType] = {\n name: file.name,\n type: layerType,\n data: result.data,\n bounds: result.bounds,\n units: result.data.units || 'mm'\n }\n }\n \n } catch (error) {\n console.warn(`解析 ${file.name} 失败:`, error)\n }\n }\n \n // 计算参考边界(从铜层、阻焊层等非 GKO/DRL 层中计算)\n // 这个边界用于选择正确的 GKO 轮廓(排除工艺边框)\n const refLayerTypes = ['GTL', 'GBL', 'GTS', 'GBS', 'GTO', 'GBO']\n for (const type of refLayerTypes) {\n const layer = layers[type]\n if (!layer || !layer.bounds) continue\n const b = layer.bounds\n \n if (!refBounds) {\n refBounds = { minX: b.minX, minY: b.minY, maxX: b.maxX, maxY: b.maxY }\n } else {\n refBounds.minX = Math.min(refBounds.minX, b.minX)\n refBounds.minY = Math.min(refBounds.minY, b.minY)\n refBounds.maxX = Math.max(refBounds.maxX, b.maxX)\n refBounds.maxY = Math.max(refBounds.maxY, b.maxY)\n }\n }\n // 内层铜(SIG2/SIG3/…):参与参考边界,便于仅内层+外形时仍能定板框\n for (const type of Object.keys(layers)) {\n if (!/^SIG\\d+$/i.test(type)) continue\n const layer = layers[type]\n if (!layer || !layer.bounds) continue\n const b = layer.bounds\n if (!refBounds) {\n refBounds = { minX: b.minX, minY: b.minY, maxX: b.maxX, maxY: b.maxY }\n } else {\n refBounds.minX = Math.min(refBounds.minX, b.minX)\n refBounds.minY = Math.min(refBounds.minY, b.minY)\n refBounds.maxX = Math.max(refBounds.maxX, b.maxX)\n refBounds.maxY = Math.max(refBounds.maxY, b.maxY)\n }\n }\n \n if (DEBUG) console.log(`[仿真图] 参考边界(铜层/阻焊层):`, refBounds)\n \n // 打印各图层的单位和边界信息\n if (DEBUG) {\n console.log(`[仿真图] ===== 各图层信息 =====`)\n for (const [type, layer] of Object.entries(layers)) {\n const b = layer.bounds\n const bStr = b ? `(${b.minX.toFixed(3)}, ${b.minY.toFixed(3)}) - (${b.maxX.toFixed(3)}, ${b.maxY.toFixed(3)})` : 'null'\n console.log(`[仿真图] ${type}: units=${layer.units}, bounds=${bStr}`)\n }\n console.log(`[仿真图] ===========================`)\n }\n \n // 计算 boardBounds:优先使用参考边界,如果没有则使用所有层(但跳过 GKO 和钻孔层)\n // GKO 层可能包含标注/箭头/文字等额外元素,不应用于边界计算\n if (refBounds) {\n boardBounds = [refBounds.minX, refBounds.minY, refBounds.maxX, refBounds.maxY]\n } else {\n // 没有参考层时,使用非 GKO/DRL 层的边界\n for (const [type, layer] of Object.entries(layers)) {\n // 跳过 GKO 层(可能包含标注元素)和钻孔层\n if (type === 'GKO' || type === 'DRL' || type === 'DRL2') continue\n if (!layer.bounds) continue\n const b = layer.bounds\n \n if (!boardBounds) {\n boardBounds = [b.minX, b.minY, b.maxX, b.maxY]\n } else {\n boardBounds[0] = Math.min(boardBounds[0], b.minX)\n boardBounds[1] = Math.min(boardBounds[1], b.minY)\n boardBounds[2] = Math.max(boardBounds[2], b.maxX)\n boardBounds[3] = Math.max(boardBounds[3], b.maxY)\n }\n }\n }\n \n if (DEBUG) console.log(`[仿真图] 板子边界:`, boardBounds)\n if (DEBUG) console.log(`[仿真图] 已加载图层:`, Object.keys(layers))\n \n // 检查并修正钻孔层对齐\n checkAndFixDrillAlignment()\n}\n\n// 钻孔层对齐检查(参考 2d-direct 的逻辑,增加特征点匹配)\nfunction checkAndFixDrillAlignment() {\n // 使用全局 refBounds(多图层合并的边界)作为参考\n // 这样钻孔对齐和渲染使用相同的参考基准\n if (!refBounds) {\n if (DEBUG) console.log('[钻孔对齐] 未找到参考边界,跳过对齐检查')\n return\n }\n \n // 为了日志显示,找一个参考层名称\n const refLayerTypes = ['GTL', 'GBL', 'GTS', 'GBS', 'GTO', 'GBO']\n let refLayerName = 'REF'\n for (const t of refLayerTypes) {\n if (layers[t] && layers[t].bounds) {\n refLayerName = t\n break\n }\n }\n if (refLayerName === 'REF') {\n const sigKeys = Object.keys(layers).filter((k) => /^SIG\\d+$/i.test(k)).sort()\n for (const t of sigKeys) {\n if (layers[t] && layers[t].bounds) {\n refLayerName = t\n break\n }\n }\n }\n \n // 辅助函数:计算边界越界情况\n const computeOverflow = (b, ref) => {\n const left = Math.max(0, ref.minX - b.minX)\n const right = Math.max(0, b.maxX - ref.maxX)\n const bottom = Math.max(0, ref.minY - b.minY)\n const top = Math.max(0, b.maxY - ref.maxY)\n const total = left + right + bottom + top\n return { left, right, bottom, top, total }\n }\n \n // 辅助函数:平移边界\n const translateBounds = (b, dx, dy) => ({\n minX: b.minX + dx,\n maxX: b.maxX + dx,\n minY: b.minY + dy,\n maxY: b.maxY + dy\n })\n \n // 辅助函数:从图层数据中提取圆形特征点(圆心坐标)\n const extractCircleCenters = (layerData, layerName = '') => {\n if (!layerData || !layerData.vertices || !layerData.lineStrips) {\n if (DEBUG) console.log(`[特征提取] ${layerName}: 数据无效`)\n return []\n }\n \n const centers = []\n const vertices = layerData.vertices\n const lineStrips = layerData.lineStrips\n \n if (DEBUG) console.log(`[特征提取] ${layerName}: 共有 ${lineStrips.length} 个 lineStrips`)\n \n let skippedTooFew = 0\n let skippedAspect = 0\n let skippedTooSmall = 0\n \n for (const strip of lineStrips) {\n if (strip.count < 6) { // 降低点数要求(从 8 降到 6)\n skippedTooFew++\n continue\n }\n \n // 计算边界框\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (let i = 0; i < strip.count; i++) {\n const ptr = (strip.start + i) * 3\n const x = vertices[ptr]\n const y = vertices[ptr + 1]\n minX = Math.min(minX, x)\n minY = Math.min(minY, y)\n maxX = Math.max(maxX, x)\n maxY = Math.max(maxY, y)\n }\n \n const w = maxX - minX\n const h = maxY - minY\n const aspectRatio = Math.max(w, h) / Math.max(0.0001, Math.min(w, h))\n \n // 判断是否接近圆形(宽高比接近 1,放宽到 2.0)\n if (aspectRatio > 2.0) {\n skippedAspect++\n continue\n }\n \n // 降低最小尺寸要求(从 0.1 降到 0.01,约 0.01mm = 10um)\n if (w < 0.01 || h < 0.01) {\n skippedTooSmall++\n continue\n }\n \n // 计算圆心\n const cx = (minX + maxX) / 2\n const cy = (minY + maxY) / 2\n const r = (w + h) / 4 // 平均半径\n centers.push({ x: cx, y: cy, r })\n }\n \n if (DEBUG) {\n console.log(`[特征提取] ${layerName}: 提取了 ${centers.length} 个圆形特征点 (跳过: 点数不足=${skippedTooFew}, 宽高比异常=${skippedAspect}, 尺寸太小=${skippedTooSmall})`)\n // 输出前 5 个特征点的详细坐标\n if (centers.length > 0) {\n const showCount = Math.min(5, centers.length)\n console.log(`[特征提取] ${layerName}: 前 ${showCount} 个特征点坐标:`)\n for (let i = 0; i < showCount; i++) {\n console.log(` ${i}: (${centers[i].x.toFixed(4)}, ${centers[i].y.toFixed(4)}), 直径=${(centers[i].r * 2).toFixed(4)}`)\n }\n }\n }\n \n \n return centers\n }\n \n // 辅助函数:使用投票机制估计偏移量(两阶段:粗匹配 + 精细化)\n const estimateOffsetByVoting = (drillCenters, refCenters, binSize = 0.5) => {\n if (drillCenters.length < 2 || refCenters.length < 2) return null\n \n // 第一阶段:粗匹配\n // 注意:仿真图使用英寸单位,binSize 最小值设为 0.02 英寸 ≈ 0.5mm\n const coarseBinSize = Math.max(binSize, 0.02)\n const votes = new Map()\n \n for (const drill of drillCenters) {\n for (const ref of refCenters) {\n const dx = ref.x - drill.x\n const dy = ref.y - drill.y\n \n const qx = Math.round(dx / coarseBinSize) * coarseBinSize\n const qy = Math.round(dy / coarseBinSize) * coarseBinSize\n const key = `${qx.toFixed(3)}_${qy.toFixed(3)}`\n \n if (!votes.has(key)) {\n votes.set(key, { dx: qx, dy: qy, count: 0, pairs: [] })\n }\n const v = votes.get(key)\n v.count++\n v.pairs.push({ drill, ref, dx, dy })\n }\n }\n \n const allVotes = Array.from(votes.values()).sort((a, b) => b.count - a.count)\n \n if (DEBUG && allVotes.length > 0) {\n const topVotes = allVotes.slice(0, 5)\n console.log(`[投票] 粗匹配前 ${topVotes.length} 个候选偏移 (binSize=${coarseBinSize}):`)\n topVotes.forEach((v, i) => {\n console.log(` ${i + 1}. dx=${v.dx.toFixed(2)}, dy=${v.dy.toFixed(2)}, votes=${v.count}`)\n })\n }\n \n if (allVotes.length === 0 || allVotes[0].count < 2) return null\n \n const bestCoarse = allVotes[0]\n \n // 第二阶段:精细化 - 使用一对一贪婪匹配(增加尺寸过滤)\n // 计算所有可能的配对及其距离,然后贪婪选择\n const tolerance = coarseBinSize * 1.5 // 容差范围\n const sizeToleranceRatio = 3.0 // 尺寸容差:焊盘尺寸不能超过钻孔的 3 倍\n const allPairs = []\n \n for (let di = 0; di < drillCenters.length; di++) {\n const drill = drillCenters[di]\n for (let ri = 0; ri < refCenters.length; ri++) {\n const ref = refCenters[ri]\n \n // 尺寸过滤:只匹配尺寸相近的钻孔和焊盘\n // 焊盘通常比钻孔大一些(焊盘环),但不应差太多\n const drillR = drill.r || 0\n const refR = ref.r || 0\n if (drillR > 0 && refR > 0) {\n const sizeRatio = Math.max(drillR, refR) / Math.min(drillR, refR)\n if (sizeRatio > sizeToleranceRatio) {\n continue // 尺寸差异太大,跳过\n }\n }\n \n // 应用粗偏移后的距离\n const shiftedDx = drill.x + bestCoarse.dx - ref.x\n const shiftedDy = drill.y + bestCoarse.dy - ref.y\n const dist = Math.sqrt(shiftedDx * shiftedDx + shiftedDy * shiftedDy)\n \n if (dist < tolerance) {\n allPairs.push({\n drillIdx: di,\n refIdx: ri,\n drill,\n ref,\n dist,\n dx: ref.x - drill.x,\n dy: ref.y - drill.y\n })\n }\n }\n }\n \n // 按距离排序\n allPairs.sort((a, b) => a.dist - b.dist)\n \n // 贪婪一对一匹配\n const usedDrill = new Set()\n const usedRef = new Set()\n const refinedPairs = []\n \n for (const pair of allPairs) {\n if (usedDrill.has(pair.drillIdx) || usedRef.has(pair.refIdx)) continue\n \n refinedPairs.push(pair)\n usedDrill.add(pair.drillIdx)\n usedRef.add(pair.refIdx)\n }\n \n if (refinedPairs.length < 2) {\n // 无法精细化,使用粗匹配的中心值\n return {\n dx: bestCoarse.dx,\n dy: bestCoarse.dy,\n votes: bestCoarse.count\n }\n }\n \n // 按照 2dPage 的方式:使用匹配对的平均值计算精确偏移量\n // 步骤1:使用粗匹配偏移量验证并获取匹配对\n // 注意:仿真图使用英寸单位,2dPage 使用毫米单位\n // 2dPage 的 tolRefine = 0.3mm,转换为英寸:0.3 / 25.4 ≈ 0.012\n const tolRefine = 0.012 // 容差 0.012 英寸 ≈ 0.3mm\n const matchedPairs = []\n const usedRefIndices = new Set()\n \n for (const drill of drillCenters) {\n const transformedX = drill.x + bestCoarse.dx\n const transformedY = drill.y + bestCoarse.dy\n \n let minDistance = Infinity\n let nearestRef = null\n let nearestIdx = -1\n \n for (let i = 0; i < refCenters.length; i++) {\n if (usedRefIndices.has(i)) continue\n const ref = refCenters[i]\n const dist = Math.sqrt(Math.pow(transformedX - ref.x, 2) + Math.pow(transformedY - ref.y, 2))\n if (dist < minDistance) {\n minDistance = dist\n nearestRef = ref\n nearestIdx = i\n }\n }\n \n if (minDistance <= tolRefine && nearestIdx !== -1) {\n usedRefIndices.add(nearestIdx)\n matchedPairs.push({\n drill,\n ref: nearestRef,\n dx: nearestRef.x - drill.x,\n dy: nearestRef.y - drill.y,\n error: minDistance\n })\n }\n }\n \n if (matchedPairs.length < 2) {\n return {\n dx: bestCoarse.dx,\n dy: bestCoarse.dy,\n votes: bestCoarse.count\n }\n }\n \n // 步骤2:使用匹配对的平均值计算精确偏移量(与 2dPage 完全一致)\n let sumDx = 0, sumDy = 0\n for (const p of matchedPairs) {\n sumDx += p.dx\n sumDy += p.dy\n }\n const finalDx = sumDx / matchedPairs.length\n const finalDy = sumDy / matchedPairs.length\n \n if (DEBUG) {\n console.log(`[投票] 精细化(2dPage方式): 粗匹配=(${bestCoarse.dx.toFixed(3)}, ${bestCoarse.dy.toFixed(3)}), 匹配对=${matchedPairs.length}/${drillCenters.length}`)\n const errors = matchedPairs.map(p => p.error)\n const avgError = errors.reduce((s, v) => s + v, 0) / errors.length\n const maxError = Math.max(...errors)\n console.log(`[投票] 匹配误差: avg=${avgError.toFixed(4)}, max=${maxError.toFixed(4)}`)\n console.log(`[投票] 配对详情:`)\n for (let i = 0; i < Math.min(matchedPairs.length, 5); i++) {\n const p = matchedPairs[i]\n console.log(` ${i}: 钻孔(${p.drill.x.toFixed(3)}, ${p.drill.y.toFixed(3)}) -> 焊盘(${p.ref.x.toFixed(3)}, ${p.ref.y.toFixed(3)}), offset=(${p.dx.toFixed(3)}, ${p.dy.toFixed(3)}), err=${p.error.toFixed(4)}`)\n }\n console.log(`[投票] 精确偏移(平均值): dx=${finalDx.toFixed(3)}, dy=${finalDy.toFixed(3)}`)\n }\n \n return {\n dx: finalDx,\n dy: finalDy,\n votes: refinedPairs.length\n }\n }\n \n // 辅助函数:验证偏移量是否正确\n const verifyOffset = (drillCenters, refCenters, dx, dy, tolerance = 0.5) => {\n let matchCount = 0\n let totalError = 0\n \n for (const drill of drillCenters) {\n const shiftedX = drill.x + dx\n const shiftedY = drill.y + dy\n \n // 找最近的参考点\n let minDist = Infinity\n for (const ref of refCenters) {\n const dist = Math.sqrt((shiftedX - ref.x) ** 2 + (shiftedY - ref.y) ** 2)\n minDist = Math.min(minDist, dist)\n }\n \n if (minDist < tolerance) {\n matchCount++\n totalError += minDist\n }\n }\n \n return {\n matchCount,\n matchRate: drillCenters.length > 0 ? matchCount / drillCenters.length : 0,\n avgError: matchCount > 0 ? totalError / matchCount : Infinity\n }\n }\n \n // 处理所有钻孔层(DRL 和 DRL2)\n const drillLayerTypes = ['DRL', 'DRL2']\n let needRecalcBounds = false\n \n for (const drlType of drillLayerTypes) {\n const drillLayer = layers[drlType]\n if (!drillLayer || !drillLayer.bounds) continue\n \n let drillBounds = drillLayer.bounds\n // 使用全局 refBounds 而不是单一 refLayer.bounds\n const refLayerBounds = refBounds\n \n const refWidth = refLayerBounds.maxX - refLayerBounds.minX\n const refHeight = refLayerBounds.maxY - refLayerBounds.minY\n let drillWidth = drillBounds.maxX - drillBounds.minX\n let drillHeight = drillBounds.maxY - drillBounds.minY\n \n const ratioX = drillWidth > 0 && refWidth > 0 ? drillWidth / refWidth : 1\n const ratioY = drillHeight > 0 && refHeight > 0 ? drillHeight / refHeight : 1\n \n if (DEBUG) {\n console.log(`[钻孔对齐] ${drlType} 边界: (${drillBounds.minX.toFixed(3)}, ${drillBounds.minY.toFixed(3)}) - (${drillBounds.maxX.toFixed(3)}, ${drillBounds.maxY.toFixed(3)})`)\n console.log(`[钻孔对齐] ${drlType} 尺寸: ${drillWidth.toFixed(3)} x ${drillHeight.toFixed(3)}`)\n console.log(`[钻孔对齐] 参考边界(合并${refLayerName}等): (${refLayerBounds.minX.toFixed(3)}, ${refLayerBounds.minY.toFixed(3)}) - (${refLayerBounds.maxX.toFixed(3)}, ${refLayerBounds.maxY.toFixed(3)})`)\n console.log(`[钻孔对齐] 参考边界尺寸: ${refWidth.toFixed(3)} x ${refHeight.toFixed(3)}`)\n console.log(`[钻孔对齐] ${drlType} 比例: X=${ratioX.toFixed(2)}, Y=${ratioY.toFixed(2)}`)\n }\n \n // 检测是否需要 10^n 缩放\n const decidePow10Scale = (ratio) => {\n if (!Number.isFinite(ratio) || ratio <= 0) return 1\n if (ratio > 2) {\n const pow10 = Math.pow(10, Math.round(Math.log10(ratio)))\n const cand = 1 / pow10\n const newRatio = ratio * cand\n if (cand !== 1 && newRatio >= 0.7 && newRatio <= 1.3) return cand\n } else if (ratio < 0.5) {\n const inv = 1 / ratio\n const pow10 = Math.pow(10, Math.round(Math.log10(inv)))\n const cand = pow10\n const newRatio = ratio * cand\n if (cand !== 1 && newRatio >= 0.7 && newRatio <= 1.3) return cand\n }\n return 1\n }\n \n const scaleX = decidePow10Scale(ratioX)\n const scaleY = decidePow10Scale(ratioY)\n \n const data = drillLayer.data\n if (!data || !data.vertices || !data.lineStrips) continue\n \n const vertices = data.vertices\n const lineStrips = data.lineStrips\n \n // 步骤1: 缩放修正\n if (scaleX !== 1 || scaleY !== 1) {\n console.warn(`[钻孔对齐] ⚠️ ${drlType} 需要缩放: scaleX=${scaleX}, scaleY=${scaleY}`)\n \n // 只缩放孔位置,不缩放孔径\n for (const strip of lineStrips) {\n if (strip.count < 3) continue\n \n // 计算当前圆心\n let sumX = 0, sumY = 0\n for (let i = 0; i < strip.count; i++) {\n const ptr = (strip.start + i) * 3\n sumX += vertices[ptr]\n sumY += vertices[ptr + 1]\n }\n const cx = sumX / strip.count\n const cy = sumY / strip.count\n \n // 计算新圆心位置\n const newCx = cx * scaleX\n const newCy = cy * scaleY\n \n // 移动所有点(只改变位置,不改变相对圆心的距离,即保持孔径不变)\n const dx = newCx - cx\n const dy = newCy - cy\n for (let i = 0; i < strip.count; i++) {\n const ptr = (strip.start + i) * 3\n vertices[ptr] += dx\n vertices[ptr + 1] += dy\n }\n }\n \n // 更新钻孔层 bounds\n drillBounds = {\n minX: drillBounds.minX * scaleX,\n maxX: drillBounds.maxX * scaleX,\n minY: drillBounds.minY * scaleY,\n maxY: drillBounds.maxY * scaleY\n }\n drillLayer.bounds = drillBounds\n drillWidth = drillBounds.maxX - drillBounds.minX\n drillHeight = drillBounds.maxY - drillBounds.minY\n \n if (DEBUG) console.log(`[钻孔对齐] ✓ ${drlType} 已缩放,新尺寸: ${drillWidth.toFixed(3)} x ${drillHeight.toFixed(3)}`)\n needRecalcBounds = true\n }\n \n // 步骤2: 特征点匹配对齐(优先于边界对齐)\n // 与 2dPage 一致:优先铜层,再阻焊层(因为阻焊常包含大量SMT开窗,不一定对应钻孔)\n const refLayerPriority = ['GTL', 'GBL', 'GTS', 'GBS']\n let featureOffset = null\n \n // 先提取钻孔特征点(只需提取一次)\n const drillCenters = extractCircleCenters(drillLayer.data, drlType)\n \n // 关键修复:对于非金属化孔(DRL2/NPTH)或孔数极少的层,跳过特征点对齐\n // np.drl (DRL2) 通常是安装孔,不对应铜层焊盘,特征点匹配会给出错误的偏移\n // PCB2.TXT (DRL) 虽然孔少(12个),但是金属化孔,需要对齐\n const isNonPlated = drlType === 'DRL2' || (drillLayer.name && /np|non-plated/i.test(drillLayer.name))\n const skipFeatureAlignment = isNonPlated || drillCenters.length < 3\n \n if (skipFeatureAlignment) {\n if (DEBUG) console.log(`[钻孔对齐] ${drlType}: 跳过特征点对齐 (isNonPlated=${isNonPlated}, count=${drillCenters.length})`)\n }\n \n if (DEBUG) console.log(`[钻孔对齐] ===== 开始特征点匹配 =====`)\n if (DEBUG) console.log(`[钻孔对齐] ${drlType} 边界中心: (${((drillBounds.minX + drillBounds.maxX) / 2).toFixed(3)}, ${((drillBounds.minY + drillBounds.maxY) / 2).toFixed(3)})`)\n \n // 如果是非金属化孔或孔数太少,跳过特征点匹配\n if (skipFeatureAlignment) {\n if (DEBUG) console.log(`[钻孔对齐] ===== 跳过特征点匹配 (DRL2/NPTH 或 count < 3) =====`)\n }\n \n for (const refType of refLayerPriority) {\n // 跳过特征点匹配\n if (skipFeatureAlignment) break\n const refLayer = layers[refType]\n if (!refLayer || !refLayer.data) {\n if (DEBUG) console.log(`[钻孔对齐] ${refType}: 图层不存在或无数据`)\n continue\n }\n \n const refCenters = extractCircleCenters(refLayer.data, refType)\n \n if (DEBUG) {\n const refBds = refLayer.bounds\n if (refBds) {\n console.log(`[钻孔对齐] ${refType} 边界中心: (${((refBds.minX + refBds.maxX) / 2).toFixed(3)}, ${((refBds.minY + refBds.maxY) / 2).toFixed(3)})`)\n }\n }\n \n if (drillCenters.length < 2 || refCenters.length < 2) {\n if (DEBUG) console.log(`[钻孔对齐] ${refType}: 特征点不足 (drill=${drillCenters.length}, ref=${refCenters.length})`)\n continue\n }\n \n // 使用投票机制估计偏移\n // 注意:仿真图使用英寸单位,2dPage 使用毫米单位\n // 2dPage 的 binSize = 0.2mm,转换为英寸:0.2 / 25.4 ≈ 0.008\n // 但考虑到可能存在较大偏移,使用稍大的值 0.02 英寸 ≈ 0.5mm\n const offset = estimateOffsetByVoting(drillCenters, refCenters, 0.02)\n if (!offset) {\n if (DEBUG) console.log(`[钻孔对齐] ${refType}: 投票未得到结果`)\n continue\n }\n \n // 验证偏移量(使用更大的容差)\n const verification = verifyOffset(drillCenters, refCenters, offset.dx, offset.dy, 1.0) // 增大容差\n \n if (DEBUG) console.log(`[钻孔对齐] ${refType} 投票结果: dx=${offset.dx.toFixed(3)}, dy=${offset.dy.toFixed(3)}, votes=${offset.votes}, matchRate=${(verification.matchRate * 100).toFixed(1)}%`)\n \n // 如果匹配率足够高(>30%)且投票数足够,使用此偏移\n if (verification.matchRate > 0.3 && offset.votes >= 2) {\n featureOffset = {\n dx: offset.dx,\n dy: offset.dy,\n matchRate: verification.matchRate,\n refType\n }\n if (DEBUG) console.log(`[钻孔对齐] ✓ 选择 ${refType} 作为参考层进行特征点对齐`)\n break\n }\n }\n \n if (DEBUG) console.log(`[钻孔对齐] ===== 特征点匹配结束 =====`)\n \n // 如果特征点匹配成功,应用偏移\n if (featureOffset && (Math.abs(featureOffset.dx) > 0.3 || Math.abs(featureOffset.dy) > 0.3)) {\n console.warn(`[钻孔对齐] ⚠️ ${drlType} 应用特征点对齐(参考${featureOffset.refType}): X=${featureOffset.dx.toFixed(3)}, Y=${featureOffset.dy.toFixed(3)} (匹配率=${(featureOffset.matchRate * 100).toFixed(1)}%)`)\n \n // 应用偏移到所有顶点\n for (let k = 0; k < vertices.length; k += 3) {\n vertices[k] += featureOffset.dx\n vertices[k + 1] += featureOffset.dy\n }\n \n // 更新 bounds\n drillLayer.bounds = translateBounds(drillBounds, featureOffset.dx, featureOffset.dy)\n \n if (DEBUG) {\n const newBounds = drillLayer.bounds\n console.log(`[钻孔对齐] ✓ ${drlType} 特征点对齐后边界: (${newBounds.minX.toFixed(3)}, ${newBounds.minY.toFixed(3)}) - (${newBounds.maxX.toFixed(3)}, ${newBounds.maxY.toFixed(3)})`)\n \n // 验证顶点确实被修改了 - 输出第一个 lineStrip 的中心\n if (lineStrips && lineStrips.length > 0) {\n const firstStrip = lineStrips[0]\n let sumX = 0, sumY = 0\n for (let i = 0; i < firstStrip.count; i++) {\n const ptr = (firstStrip.start + i) * 3\n sumX += vertices[ptr]\n sumY += vertices[ptr + 1]\n }\n const cx = sumX / firstStrip.count\n const cy = sumY / firstStrip.count\n console.log(`[钻孔对齐] 验证: 第一个钻孔中心修正后 = (${cx.toFixed(3)}, ${cy.toFixed(3)})`)\n \n // 同时输出 GTS 的第一个焊盘坐标作为对比\n const gtsLayer = layers.GTS\n if (gtsLayer && gtsLayer.data && gtsLayer.data.lineStrips && gtsLayer.data.lineStrips.length > 0) {\n const gtsVerts = gtsLayer.data.vertices\n const gtsStrip = gtsLayer.data.lineStrips[0]\n let gtsMinX = Infinity, gtsMinY = Infinity, gtsMaxX = -Infinity, gtsMaxY = -Infinity\n for (let i = 0; i < gtsStrip.count; i++) {\n const ptr = (gtsStrip.start + i) * 3\n gtsMinX = Math.min(gtsMinX, gtsVerts[ptr])\n gtsMaxX = Math.max(gtsMaxX, gtsVerts[ptr])\n gtsMinY = Math.min(gtsMinY, gtsVerts[ptr + 1])\n gtsMaxY = Math.max(gtsMaxY, gtsVerts[ptr + 1])\n }\n const gtsCx = (gtsMinX + gtsMaxX) / 2\n const gtsCy = (gtsMinY + gtsMaxY) / 2\n console.log(`[钻孔对齐] 对比: GTS 第一个形状中心 = (${gtsCx.toFixed(3)}, ${gtsCy.toFixed(3)})`)\n }\n }\n }\n \n needRecalcBounds = true\n continue // 特征点对齐成功,跳过边界对齐\n }\n \n // 步骤3: 边界对齐(作为兜底)\n // 计算中心距离\n const drillCenterX = (drillBounds.minX + drillBounds.maxX) / 2\n const drillCenterY = (drillBounds.minY + drillBounds.maxY) / 2\n const refCenterX = (refLayerBounds.minX + refLayerBounds.maxX) / 2\n const refCenterY = (refLayerBounds.minY + refLayerBounds.maxY) / 2\n const centerDistX = Math.abs(drillCenterX - refCenterX)\n const centerDistY = Math.abs(drillCenterY - refCenterY)\n const centerDist = Math.sqrt(centerDistX * centerDistX + centerDistY * centerDistY)\n \n // 计算尺寸比例(用于判断是否是同一块板)\n const sizeRatioX = drillWidth > 0 && refWidth > 0 ? Math.min(drillWidth / refWidth, refWidth / drillWidth) : 0\n const sizeRatioY = drillHeight > 0 && refHeight > 0 ? Math.min(drillHeight / refHeight, refHeight / drillHeight) : 0\n \n if (DEBUG) console.log(`[钻孔对齐] ${drlType} 中心距离: X=${centerDistX.toFixed(3)}, Y=${centerDistY.toFixed(3)}, total=${centerDist.toFixed(3)}`)\n if (DEBUG) console.log(`[钻孔对齐] ${drlType} 尺寸比例: X=${sizeRatioX.toFixed(3)}, Y=${sizeRatioY.toFixed(3)}`)\n \n const overflowBefore = computeOverflow(drillBounds, refLayerBounds)\n if (DEBUG) console.log(`[钻孔对齐] ${drlType} Overflow(before): left=${overflowBefore.left.toFixed(3)} right=${overflowBefore.right.toFixed(3)} bottom=${overflowBefore.bottom.toFixed(3)} top=${overflowBefore.top.toFixed(3)} total=${overflowBefore.total.toFixed(3)}`)\n \n // 判断是否需要对齐:\n // 1. 如果钻孔层完全在参考层边界内(没有越界),不需要对齐\n // 2. 中心距离超过阈值(表明钻孔层与参考层不重合)\n // 3. 或者越界超过阈值\n // 4. 且尺寸比例合理(表明是同一块板,不是完全不同的坐标系)\n const applyThresholdMm = 0.5\n const centerThresholdMm = 2.0 // 中心距离阈值\n \n // 重要:如果钻孔层完全在参考层边界内,不需要任何对齐\n const isFullyContained = overflowBefore.total < 0.01\n if (isFullyContained) {\n if (DEBUG) console.log(`[钻孔对齐] ✓ ${drlType} 完全在参考层边界内,无需对齐`)\n continue // 跳过此钻孔层的对齐处理\n }\n \n const needsAlignment = (centerDist > centerThresholdMm || overflowBefore.total > applyThresholdMm) && \n sizeRatioX > 0.5 && sizeRatioY > 0.5\n \n if (needsAlignment) {\n // 策略选择:\n // 1. 如果尺寸比例很接近(>0.85),说明是同一块板,优先使用中心对齐\n // 2. 否则使用边界对齐(选择使 overflow 最小的偏移)\n const useCenterAlign = sizeRatioX > 0.85 && sizeRatioY > 0.85\n \n let finalDx = 0, finalDy = 0\n \n if (useCenterAlign) {\n // 中心对齐:直接将钻孔层中心移动到参考层中心\n finalDx = refCenterX - drillCenterX\n finalDy = refCenterY - drillCenterY\n if (DEBUG) console.log(`[钻孔对齐] ${drlType} 尺寸比例接近(${sizeRatioX.toFixed(2)}, ${sizeRatioY.toFixed(2)}),使用中心对齐`)\n } else {\n // 边界对齐:计算多个候选偏移,选择使越界最小的\n const candidatesX = [\n 0,\n refLayerBounds.minX - drillBounds.minX, // 左边对齐\n refLayerBounds.maxX - drillBounds.maxX, // 右边对齐\n refCenterX - drillCenterX // 中心对齐\n ]\n const candidatesY = [\n 0,\n refLayerBounds.minY - drillBounds.minY, // 底边对齐\n refLayerBounds.maxY - drillBounds.maxY, // 顶边对齐\n refCenterY - drillCenterY // 中心对齐\n ]\n \n let best = { dx: 0, dy: 0, error: Infinity, shift: Infinity }\n for (const dx of candidatesX) {\n for (const dy of candidatesY) {\n const afterBounds = translateBounds(drillBounds, dx, dy)\n const ov = computeOverflow(afterBounds, refLayerBounds)\n const shift = Math.abs(dx) + Math.abs(dy)\n if (ov.total < best.error - 1e-6 || (Math.abs(ov.total - best.error) < 1e-6 && shift < best.shift)) {\n best = { dx, dy, error: ov.total, shift }\n }\n }\n }\n finalDx = best.dx\n finalDy = best.dy\n if (DEBUG) console.log(`[钻孔对齐] ${drlType} 使用边界对齐,候选偏移: X=${best.dx.toFixed(3)}, Y=${best.dy.toFixed(3)} (overflow after=${best.error.toFixed(3)})`)\n }\n \n // 应用偏移\n if (Math.abs(finalDx) >= applyThresholdMm || Math.abs(finalDy) >= applyThresholdMm) {\n console.warn(`[钻孔对齐] ⚠️ ${drlType} 应用${useCenterAlign ? '中心' : '边界'}对齐: X=${finalDx.toFixed(3)}, Y=${finalDy.toFixed(3)}`)\n \n // 应用偏移到所有顶点\n for (let k = 0; k < vertices.length; k += 3) {\n vertices[k] += finalDx\n vertices[k + 1] += finalDy\n }\n \n // 更新 bounds\n drillLayer.bounds = translateBounds(drillBounds, finalDx, finalDy)\n \n const overflowAfter = computeOverflow(drillLayer.bounds, refLayerBounds)\n if (DEBUG) console.log(`[钻孔对齐] ✓ ${drlType} Overflow(after): left=${overflowAfter.left.toFixed(3)} right=${overflowAfter.right.toFixed(3)} bottom=${overflowAfter.bottom.toFixed(3)} top=${overflowAfter.top.toFixed(3)} total=${overflowAfter.total.toFixed(3)}`)\n \n needRecalcBounds = true\n } else {\n if (DEBUG) console.log(`[钻孔对齐] ${drlType} 偏移不显著,跳过`)\n }\n } else {\n if (DEBUG) console.log(`[钻孔对齐] ✓ ${drlType} 已对齐(中心距离 < ${centerThresholdMm}mm,越界 < ${applyThresholdMm}mm)`)\n }\n }\n \n if (needRecalcBounds) {\n recalculateBoardBounds()\n }\n}\n\n// 重新计算板子边界(排除钻孔层和 GKO 层)\nfunction recalculateBoardBounds() {\n // 优先使用 refBounds(来自铜层/阻焊层)\n if (refBounds) {\n boardBounds = [refBounds.minX, refBounds.minY, refBounds.maxX, refBounds.maxY]\n if (DEBUG) console.log(`[仿真图] 重新计算板子边界(使用参考边界):`, boardBounds)\n return\n }\n \n boardBounds = null\n for (const [type, layer] of Object.entries(layers)) {\n // 跳过 GKO 层(可能包含标注元素)和钻孔层\n if (type === 'DRL' || type === 'DRL2' || type === 'GKO') continue\n if (!layer.bounds) continue\n \n const b = layer.bounds\n if (!boardBounds) {\n boardBounds = [b.minX, b.minY, b.maxX, b.maxY]\n } else {\n boardBounds[0] = Math.min(boardBounds[0], b.minX)\n boardBounds[1] = Math.min(boardBounds[1], b.minY)\n boardBounds[2] = Math.max(boardBounds[2], b.maxX)\n boardBounds[3] = Math.max(boardBounds[3], b.maxY)\n }\n }\n if (DEBUG) console.log(`[仿真图] 重新计算板子边界:`, boardBounds)\n}\n\nfunction getLayerType(fileName) {\n const name = fileName.toLowerCase()\n const ext = '.' + name.split('.').pop()\n \n // 标准 Gerber 扩展名\n if (ext === '.gtl') return 'GTL'\n if (ext === '.gbl') return 'GBL'\n if (ext === '.gts') return 'GTS'\n if (ext === '.gbs') return 'GBS'\n if (ext === '.gto') return 'GTO'\n if (ext === '.gbo') return 'GBO'\n if (ext === '.gtp') return 'GTP'\n if (ext === '.gbp') return 'GBP'\n if (ext === '.gko' || ext === '.gm1') return 'GKO'\n \n // 钻孔文件识别\n if (ext === '.drl' || ext === '.txt') {\n // 区分金属化孔(p.drl)和非金属化孔(np.drl)\n const fileNameOnly = fileName.split(/[/\\\\]/).pop().toLowerCase()\n const baseName = fileNameOnly.replace(/\\.[^.]+$/, '') // 去掉扩展名\n if (baseName === 'np' || baseName.startsWith('np-') || baseName.endsWith('-np')) {\n return 'DRL2' // 非金属化孔\n }\n return 'DRL' // 金属化孔\n }\n \n // CAM350 等软件导出的扩展名映射\n if (ext === '.bot') return 'GBL' // 底层铜\n if (ext === '.top') return 'GTL' // 顶层铜\n if (ext === '.sob') return 'GBS' // 底层阻焊 (Solder mask Bottom)\n if (ext === '.sot') return 'GTS' // 顶层阻焊 (Solder mask Top)\n if (ext === '.sst') return 'GTO' // 顶层丝印 (Silkscreen Top)\n if (ext === '.ssb') return 'GBO' // 底层丝印 (Silkscreen Bottom)\n if (ext === '.smt') return 'GTS' // 顶层阻焊 (Solder Mask Top)\n if (ext === '.smb') return 'GBS' // 底层阻焊 (Solder Mask Bottom)\n if (ext === '.pmt') return 'GTP' // 顶层锡膏 (Paste Mask Top)\n if (ext === '.pmb') return 'GBP' // 底层锡膏 (Paste Mask Bottom)\n if (ext === '.ser') return 'GTO' // 丝印层(假设为顶层丝印)\n if (ext === '.dri') return 'DRL' // 钻孔文件\n if (ext === '.d') return 'DRL' // 钻孔文件\n \n // .art 文件:按文件名(不含扩展名)映射为标准层类型\n // Allegro/Cadence 输出格式:SST.art, SMT.art, TOP.art, BOT.art, SMB.art, SSB.art, OUT.art, LAY2.art, LAY3.art...\n if (ext === '.art') {\n // 提取文件名(不含扩展名)\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const dot = fileNameOnly.lastIndexOf('.')\n const stemUpper = (dot > 0 ? fileNameOnly.substring(0, dot) : fileNameOnly).toUpperCase()\n \n if (stemUpper === 'SST') return 'GTO'\n if (stemUpper === 'SMT') return 'GTS'\n if (stemUpper === 'TOP') return 'GTL'\n if (stemUpper === 'BOT') return 'GBL'\n if (stemUpper === 'SMB') return 'GBS'\n if (stemUpper === 'SSB') return 'GBO'\n if (stemUpper === 'OUT') return 'GKO'\n // LAY2, LAY3, ... -> 中间铜层(3D仿真图不显示中间层,返回 null)\n const layMatch = stemUpper.match(/^LAY(\\d+)$/)\n if (layMatch) {\n return null // 中间层不渲染\n }\n // 其他未知的 .art 文件不渲染\n return null\n }\n \n // .pho 文件:根据文件名前缀和数字大小识别图层类型\n // Genesis/CAM 系统输出格式:sst001026.pho, sm001021.pho, smd001023.pho, ssb002029.pho, art001xxx.pho 等\n // 规则:前缀后的数字,前3位判断顶层/底层(001=顶层,002=底层)\n if (ext === '.pho') {\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const nameUpper = fileNameOnly.toUpperCase()\n \n // 辅助函数:判断是顶层还是底层(按数字前3位:001=顶层,002及以上=底层)\n const isTopLayer = (numberStr) => {\n if (!numberStr) return true\n // 取前3位数字\n const prefix3 = numberStr.substring(0, 3)\n const num = parseInt(prefix3, 10)\n return num <= 1 // 001 或 000 是顶层,002 及以上是底层\n }\n \n // sst 开头 → GTO (顶层丝印)\n if (nameUpper.startsWith('SST')) {\n return 'GTO'\n }\n \n // ssb 开头 → GBO (底层丝印)\n if (nameUpper.startsWith('SSB')) {\n return 'GBO'\n }\n \n // smd 开头,根据数字判断顶层/底层锡膏\n if (nameUpper.startsWith('SMD')) {\n const numberMatch = nameUpper.match(/^SMD(\\d+)/)\n if (numberMatch) {\n return isTopLayer(numberMatch[1]) ? 'GTP' : 'GBP'\n }\n return 'GTP' // 没有数字,默认 GTP\n }\n \n // sm 开头(注意要在 smd 之后判断),根据数字判断顶层/底层阻焊\n if (nameUpper.startsWith('SM')) {\n const numberMatch = nameUpper.match(/^SM(\\d+)/)\n if (numberMatch) {\n return isTopLayer(numberMatch[1]) ? 'GTS' : 'GBS'\n }\n return 'GTS' // 没有数字,默认 GTS\n }\n \n // art 开头,根据数字判断顶层/底层铜\n if (nameUpper.startsWith('ART')) {\n const numberMatch = nameUpper.match(/^ART(\\d+)/)\n if (numberMatch) {\n return isTopLayer(numberMatch[1]) ? 'GTL' : 'GBL'\n }\n return 'GTL' // 没有数字,默认 GTL\n }\n \n // drl 开头 → DRL (钻孔)\n if (nameUpper.startsWith('DRL')) {\n return 'DRL'\n }\n \n // out/outline 开头 → GKO (轮廓)\n if (nameUpper.startsWith('OUT')) {\n return 'GKO'\n }\n \n // 其他 .pho 文件不渲染\n return null\n }\n\n // .gbr:KiCad(内层/顶底/外形)\n if (ext === '.gbr') {\n const fileNameOnly = fileName.split(/[/\\\\]/).pop() || ''\n const nu = fileNameOnly.toUpperCase()\n if (/EDGE[._-]CUTS/i.test(nu)) return 'GKO'\n if (/F[._-]CU\\b/i.test(nu)) return 'GTL'\n if (/B[._-]CU\\b/i.test(nu)) return 'GBL'\n const inM = nu.match(/IN(\\d+)[._-]CU/i)\n if (inM) return 'SIG' + (parseInt(inM[1], 10) + 1)\n }\n\n // KiCad/Protel 内层:.g2/.g3…,常与 In1_Cu / In2_Cu 文件名同现\n if (/^\\.g\\d+$/.test(ext)) {\n const fileNameOnly = fileName.split(/[/\\\\]/).pop() || ''\n const nu = fileNameOnly.toUpperCase()\n const inM = nu.match(/IN(\\d+)[._-]CU/i)\n if (inM) return 'SIG' + (parseInt(inM[1], 10) + 1)\n const gd = ext.match(/^\\.g(\\d+)$/)\n if (gd) return 'SIG' + (parseInt(gd[1], 10) + 1)\n }\n if (/^\\.gp\\d+$/.test(ext)) {\n const gpd = ext.match(/^\\.gp(\\d+)$/)\n if (gpd) return 'GP' + parseInt(gpd[1], 10)\n }\n \n // 文件名模式匹配\n if (name.includes('top') && name.includes('copper')) return 'GTL'\n if (name.includes('bottom') && name.includes('copper')) return 'GBL'\n if (name.includes('top') && name.includes('mask')) return 'GTS'\n if (name.includes('bottom') && name.includes('mask')) return 'GBS'\n if (name.includes('top') && name.includes('silk')) return 'GTO'\n if (name.includes('bottom') && name.includes('silk')) return 'GBO'\n if (name.includes('outline') || name.includes('profile')) return 'GKO'\n if (name.includes('drill')) return 'DRL'\n \n return null\n}\n\n// ==================== 仿真图渲染 ====================\n\n// 使用 requestAnimationFrame 的防抖渲染\nfunction render() {\n if (renderRAF) {\n cancelAnimationFrame(renderRAF)\n }\n renderRAF = requestAnimationFrame(doRender)\n}\n\n// 标记缓存失效(数据变化时调用)\nfunction invalidateCache() {\n layerCache.dirty = true\n layerCache.top = null\n layerCache.bottom = null\n}\n\n// 实际渲染函数\nfunction doRender() {\n renderRAF = null\n \n const container = document.getElementById('canvasContainer')\n const containerWidth = container.clientWidth\n const containerHeight = container.clientHeight\n const pixelRatio = window.devicePixelRatio || 1\n \n // 设置画布大小为容器大小\n canvas.width = containerWidth * pixelRatio\n canvas.height = containerHeight * pixelRatio\n canvas.style.width = `${containerWidth}px`\n canvas.style.height = `${containerHeight}px`\n \n // 清空画布,纯黑背景\n ctx.fillStyle = '#1a1a1a'\n ctx.fillRect(0, 0, canvas.width, canvas.height)\n \n if (!boardBounds || Object.keys(layers).length === 0) return\n \n const boardWidth = boardBounds[2] - boardBounds[0]\n const boardHeight = boardBounds[3] - boardBounds[1]\n if (DEBUG) console.log(`[仿真图] render: boardBounds = [${boardBounds.map(v => v.toFixed(2)).join(', ')}], 尺寸 = ${boardWidth.toFixed(2)} x ${boardHeight.toFixed(2)}`)\n \n const firstLayer = Object.values(layers)[0]\n unitScale = (firstLayer && (firstLayer.units === 'in' || firstLayer.units === 'inch')) ? 25.4 : 1\n \n const displayWidth = boardWidth * unitScale\n const displayHeight = boardHeight * unitScale\n \n const baseScale = Math.min(\n containerWidth / displayWidth,\n containerHeight / displayHeight\n ) * 0.9\n \n const finalScale = baseScale * pixelRatio * unitScale * zoomLevel\n \n // 当放大时(zoomLevel > 1),直接渲染以保证清晰度\n // 当缩小或1倍时,可以使用缓存\n const useDirectRender = zoomLevel > 1.01\n \n if (useDirectRender) {\n // 直接渲染(高质量)\n renderDirect(ctx, canvas.width, canvas.height, boardBounds, boardWidth, boardHeight, finalScale, baseScale, displayWidth, displayHeight, pixelRatio)\n } else {\n // 检查是否需要重建缓存\n const needRebuildCache = layerCache.dirty || \n layerCache.lastWidth !== canvas.width || \n layerCache.lastHeight !== canvas.height\n \n if (needRebuildCache) {\n const baseRenderScale = baseScale * pixelRatio * unitScale\n layerCache.top = createViewCache('top', canvas.width, canvas.height, boardBounds, boardWidth, boardHeight, baseRenderScale, baseScale, displayWidth, displayHeight, pixelRatio)\n layerCache.bottom = createViewCache('bottom', canvas.width, canvas.height, boardBounds, boardWidth, boardHeight, baseRenderScale, baseScale, displayWidth, displayHeight, pixelRatio)\n layerCache.dirty = false\n layerCache.lastWidth = canvas.width\n layerCache.lastHeight = canvas.height\n }\n \n // 从缓存绘制\n const cachedCanvas = currentView === 'top' ? layerCache.top : layerCache.bottom\n if (cachedCanvas) {\n const centerX = canvas.width / 2\n const centerY = canvas.height / 2\n \n ctx.save()\n ctx.imageSmoothingEnabled = false // 禁用平滑,保持锐利\n ctx.translate(centerX, centerY)\n ctx.scale(zoomLevel, zoomLevel)\n ctx.translate(-centerX + panOffset.x * pixelRatio, -centerY + panOffset.y * pixelRatio)\n ctx.drawImage(cachedCanvas, 0, 0)\n ctx.restore()\n }\n }\n}\n\n// 直接渲染(不使用缓存,用于放大时保证清晰度)\nfunction renderDirect(ctx, width, height, boardBounds, boardWidth, boardHeight, finalScale, baseScale, displayWidth, displayHeight, pixelRatio) {\n const containerWidth = width / pixelRatio\n const containerHeight = height / pixelRatio\n const scaledWidth = displayWidth * baseScale * zoomLevel\n const scaledHeight = displayHeight * baseScale * zoomLevel\n const offsetX = (containerWidth - scaledWidth) / 2 * pixelRatio + panOffset.x * pixelRatio\n const offsetY = (containerHeight - scaledHeight) / 2 * pixelRatio + panOffset.y * pixelRatio\n \n ctx.save()\n ctx.translate(offsetX, offsetY)\n \n const isTop = currentView === 'top'\n if (isTop) {\n ctx.scale(finalScale, finalScale)\n ctx.translate(-boardBounds[0], -boardBounds[1])\n } else {\n ctx.translate(scaledWidth * pixelRatio, 0)\n ctx.scale(-finalScale, finalScale)\n ctx.translate(-boardBounds[0], -boardBounds[1])\n }\n \n const copperLayer = isTop ? layers.GTL : layers.GBL\n const maskLayer = isTop ? layers.GTS : layers.GBS\n const silkLayer = isTop ? layers.GTO : layers.GBO\n const outlineLayer = layers.GKO\n const drillLayer = layers.DRL\n \n // 1. 深绿色板边背景\n ctx.fillStyle = '#226B0E'\n ctx.fillRect(boardBounds[0], boardBounds[1], boardWidth, boardHeight)\n \n // 2. PCB 主体\n if (outlineLayer && outlineLayer.data) {\n ctx.fillStyle = '#2d5a28'\n drawOutlineFillAllClosed(ctx, outlineLayer.data)\n } else {\n ctx.fillStyle = '#2d5a28'\n ctx.fillRect(boardBounds[0], boardBounds[1], boardWidth, boardHeight)\n }\n \n // 3. 铜层走线\n if (copperLayer && copperLayer.data) {\n ctx.fillStyle = '#40721E'\n ctx.strokeStyle = '#40721E'\n ctx.lineWidth = 0.08 / unitScale\n drawLayerData(ctx, copperLayer.data, true)\n }\n \n // 4. 阻焊层覆盖\n if (config.showMask) {\n ctx.fillStyle = hexToRgba(config.maskColor, 0.65)\n if (outlineLayer && outlineLayer.data) {\n drawOutlineFillAllClosed(ctx, outlineLayer.data)\n } else {\n ctx.fillRect(boardBounds[0], boardBounds[1], boardWidth, boardHeight)\n }\n }\n \n // 5. 焊盘\n if (maskLayer && maskLayer.data) {\n ctx.fillStyle = FINISH_COLORS[config.surfaceFinish]\n ctx.strokeStyle = FINISH_COLORS[config.surfaceFinish]\n drawLayerData(ctx, maskLayer.data, false)\n }\n \n // 6. 丝印层\n if (config.showSilk && silkLayer && silkLayer.data) {\n ctx.fillStyle = config.silkColor\n ctx.strokeStyle = config.silkColor\n ctx.lineWidth = 0.4 / unitScale\n drawLayerData(ctx, silkLayer.data, false)\n }\n \n // 7. 钻孔\n if (config.showDrill) {\n ctx.fillStyle = '#1a1a1a'\n if (drillLayer && drillLayer.data) {\n drawDrillHoles(ctx, drillLayer.data)\n }\n const drillLayer2 = layers.DRL2\n if (drillLayer2 && drillLayer2.data) {\n drawDrillHoles(ctx, drillLayer2.data)\n }\n }\n \n ctx.restore()\n}\n\n// 创建单个视图的缓存(基础缩放级别,zoom=1)\nfunction createViewCache(view, width, height, boardBounds, boardWidth, boardHeight, baseRenderScale, baseScale, displayWidth, displayHeight, pixelRatio) {\n const offscreen = document.createElement('canvas')\n offscreen.width = width\n offscreen.height = height\n const offCtx = offscreen.getContext('2d')\n \n // 清空\n offCtx.fillStyle = '#1a1a1a'\n offCtx.fillRect(0, 0, width, height)\n \n const containerWidth = width / pixelRatio\n const containerHeight = height / pixelRatio\n // 使用基础缩放(不包含 zoomLevel)\n const scaledWidth = displayWidth * baseScale\n const scaledHeight = displayHeight * baseScale\n const offsetX = (containerWidth - scaledWidth) / 2 * pixelRatio\n const offsetY = (containerHeight - scaledHeight) / 2 * pixelRatio\n \n offCtx.save()\n offCtx.translate(offsetX, offsetY)\n \n const isTop = view === 'top'\n if (isTop) {\n offCtx.scale(baseRenderScale, baseRenderScale)\n offCtx.translate(-boardBounds[0], -boardBounds[1])\n } else {\n offCtx.translate(scaledWidth * pixelRatio, 0)\n offCtx.scale(-baseRenderScale, baseRenderScale)\n offCtx.translate(-boardBounds[0], -boardBounds[1])\n }\n \n const copperLayer = isTop ? layers.GTL : layers.GBL\n const maskLayer = isTop ? layers.GTS : layers.GBS\n const silkLayer = isTop ? layers.GTO : layers.GBO\n const outlineLayer = layers.GKO\n const drillLayer = layers.DRL\n \n // 1. 深绿色板边背景(轮廓外的区域)\n offCtx.fillStyle = '#226B0E'\n offCtx.fillRect(boardBounds[0], boardBounds[1], boardWidth, boardHeight)\n \n // 2. PCB 主体(深绿色)\n if (outlineLayer && outlineLayer.data) {\n offCtx.fillStyle = '#2d5a28'\n drawOutlineFillAllClosed(offCtx, outlineLayer.data)\n } else {\n offCtx.fillStyle = '#2d5a28'\n offCtx.fillRect(boardBounds[0], boardBounds[1], boardWidth, boardHeight)\n }\n \n // 3. 铜层走线(浅绿色)\n if (copperLayer && copperLayer.data) {\n offCtx.fillStyle = '#40721E'\n offCtx.strokeStyle = '#40721E'\n offCtx.lineWidth = 0.08 / unitScale\n drawLayerData(offCtx, copperLayer.data, true)\n }\n \n // 4. 阻焊层覆盖(半透明绿色)\n if (config.showMask) {\n offCtx.fillStyle = hexToRgba(config.maskColor, 0.65)\n if (outlineLayer && outlineLayer.data) {\n drawOutlineFillAllClosed(offCtx, outlineLayer.data)\n } else {\n offCtx.fillRect(boardBounds[0], boardBounds[1], boardWidth, boardHeight)\n }\n }\n \n // 5. 焊盘\n if (maskLayer && maskLayer.data) {\n // 调试:输出 GTS 的前 3 个形状坐标\n if (DEBUG && view === 'top' && maskLayer.data.lineStrips && maskLayer.data.lineStrips.length > 0) {\n const gtsVerts = maskLayer.data.vertices\n const showCount = Math.min(3, maskLayer.data.lineStrips.length)\n console.log(`[焊盘渲染] GTS 前 ${showCount} 个形状坐标:`)\n for (let i = 0; i < showCount; i++) {\n const strip = maskLayer.data.lineStrips[i]\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (let j = 0; j < strip.count; j++) {\n const ptr = (strip.start + j) * 3\n minX = Math.min(minX, gtsVerts[ptr])\n maxX = Math.max(maxX, gtsVerts[ptr])\n minY = Math.min(minY, gtsVerts[ptr + 1])\n maxY = Math.max(maxY, gtsVerts[ptr + 1])\n }\n const cx = (minX + maxX) / 2\n const cy = (minY + maxY) / 2\n console.log(` GTS形状${i}: 中心=(${cx.toFixed(4)}, ${cy.toFixed(4)}), 尺寸=${(maxX-minX).toFixed(4)}x${(maxY-minY).toFixed(4)}`)\n }\n }\n offCtx.fillStyle = FINISH_COLORS[config.surfaceFinish]\n offCtx.strokeStyle = FINISH_COLORS[config.surfaceFinish]\n drawLayerData(offCtx, maskLayer.data, false)\n }\n \n // 6. 丝印层\n if (config.showSilk && silkLayer && silkLayer.data) {\n offCtx.fillStyle = config.silkColor\n offCtx.strokeStyle = config.silkColor\n offCtx.lineWidth = 0.4 / unitScale\n drawLayerData(offCtx, silkLayer.data, false)\n }\n \n // 7. 钻孔(支持多个钻孔层:DRL、DRL2)\n if (config.showDrill) {\n offCtx.fillStyle = '#1a1a1a'\n \n if (drillLayer && drillLayer.data) {\n if (DEBUG) console.log(`[仿真图] 渲染钻孔层 DRL (${view}):`, drillLayer.data.lineStrips?.length)\n drawDrillHoles(offCtx, drillLayer.data)\n }\n \n const drillLayer2 = layers.DRL2\n if (drillLayer2 && drillLayer2.data) {\n if (DEBUG) console.log(`[仿真图] 渲染钻孔层 DRL2 (${view}):`, drillLayer2.data.lineStrips?.length)\n drawDrillHoles(offCtx, drillLayer2.data)\n }\n }\n \n offCtx.restore()\n return offscreen\n}\n\nfunction drawLayerData(ctx, data, includeTraces = false) {\n if (!data || !data.lineStrips || !data.vertices) return\n \n const vertices = data.vertices\n const lineStrips = data.lineStrips\n \n const shapes = []\n const traces = []\n \n for (const strip of lineStrips) {\n if (strip.count < 2) continue\n \n let ptr = strip.start * 3\n const startX = vertices[ptr]\n const startY = vertices[ptr + 1]\n \n const lastPtr = (strip.start + strip.count - 1) * 3\n const lastX = vertices[lastPtr]\n const lastY = vertices[lastPtr + 1]\n \n // 优化:使用曼哈顿距离代替欧几里得距离\n const dist = Math.abs(startX - lastX) + Math.abs(startY - lastY)\n const isClosed = dist < 0.002\n \n if (isClosed) {\n // 优化:直接从 vertices 数组计算,不创建 points 数组\n let area = 0\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (let i = 0; i < strip.count; i++) {\n const p1 = (strip.start + i) * 3\n const p2 = (strip.start + (i + 1) % strip.count) * 3\n const x1 = vertices[p1], y1 = vertices[p1 + 1]\n const x2 = vertices[p2], y2 = vertices[p2 + 1]\n area += x1 * y2 - x2 * y1\n if (x1 < minX) minX = x1\n if (y1 < minY) minY = y1\n if (x1 > maxX) maxX = x1\n if (y1 > maxY) maxY = y1\n }\n area = Math.abs(area) / 2\n \n shapes.push({ strip, area, bbox: { minX, minY, maxX, maxY } })\n } else {\n traces.push(strip)\n }\n }\n \n if (shapes.length > 1 && includeTraces) {\n const totalArea = shapes.reduce((sum, s) => sum + s.area, 0)\n const maxArea = Math.max(...shapes.map(s => s.area))\n const threshold = totalArea * 0.4\n \n const filteredShapes = shapes.filter(s => !(s.area > threshold && s.area === maxArea))\n shapes.length = 0\n shapes.push(...filteredShapes)\n }\n \n // 性能优化:当形状数量很多时,跳过复杂的包含检测,直接绘制\n const SKIP_CONTAINMENT_THRESHOLD = 200\n const shapeParent = new Map()\n const shapeChildren = new Map()\n \n if (shapes.length <= SKIP_CONTAINMENT_THRESHOLD) {\n // 检测孔洞关系\n for (let i = 0; i < shapes.length; i++) {\n shapeChildren.set(i, [])\n }\n \n // 按面积从大到小排序\n const sortedIndices = shapes.map((_, i) => i).sort((a, b) => shapes[b].area - shapes[a].area)\n \n // 优化:只对前 N 个最大的形状作为候选父\n const MAX_PARENT_CANDIDATES = 50\n const parentCandidates = sortedIndices.slice(0, MAX_PARENT_CANDIDATES)\n \n for (let i = 0; i < sortedIndices.length; i++) {\n const childIdx = sortedIndices[i]\n const child = shapes[childIdx]\n \n let bestParent = -1\n let bestParentArea = Infinity\n \n // 只在候选父中查找\n for (const parentIdx of parentCandidates) {\n if (parentIdx === childIdx) continue\n \n const parent = shapes[parentIdx]\n if (parent.area <= child.area) continue\n \n const eps = 0.001\n if (child.bbox.minX >= parent.bbox.minX - eps &&\n child.bbox.maxX <= parent.bbox.maxX + eps &&\n child.bbox.minY >= parent.bbox.minY - eps &&\n child.bbox.maxY <= parent.bbox.maxY + eps) {\n if (parent.area < bestParentArea) {\n bestParentArea = parent.area\n bestParent = parentIdx\n }\n }\n }\n \n if (bestParent >= 0) {\n shapeParent.set(childIdx, bestParent)\n shapeChildren.get(bestParent).push(childIdx)\n }\n }\n } else {\n // 形状太多,跳过包含检测,所有形状都作为顶级形状\n for (let i = 0; i < shapes.length; i++) {\n shapeChildren.set(i, [])\n }\n }\n \n // 找出所有顶级 shape(没有父的)\n const topLevelShapes = []\n for (let i = 0; i < shapes.length; i++) {\n if (!shapeParent.has(i)) {\n topLevelShapes.push(i)\n }\n }\n \n // 渲染:对于每个顶级 shape,连同其直接子(孔洞)一起用 evenodd 渲染\n const rendered = new Set()\n \n for (const topIdx of topLevelShapes) {\n if (rendered.has(topIdx)) continue\n \n const shape = shapes[topIdx]\n const children = shapeChildren.get(topIdx) || []\n \n ctx.beginPath()\n \n // 添加外轮廓\n let ptr = shape.strip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n for (let i = 1; i < shape.strip.count; i++) {\n ptr = (shape.strip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n ctx.closePath()\n \n // 添加直接子(一级孔洞)\n for (const childIdx of children) {\n const child = shapes[childIdx]\n ptr = child.strip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n for (let i = 1; i < child.strip.count; i++) {\n ptr = (child.strip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n ctx.closePath()\n rendered.add(childIdx)\n \n // 孔洞内部可能还有形状(嵌套岛),这些需要单独渲染\n const grandChildren = shapeChildren.get(childIdx) || []\n for (const grandIdx of grandChildren) {\n if (!rendered.has(grandIdx)) {\n // 将这个孙子提升为顶级渲染\n topLevelShapes.push(grandIdx)\n }\n }\n }\n \n ctx.fill('evenodd')\n rendered.add(topIdx)\n }\n \n if (includeTraces) {\n for (const strip of traces) {\n ctx.beginPath()\n \n let ptr = strip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n \n for (let i = 1; i < strip.count; i++) {\n ptr = (strip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n \n ctx.stroke()\n }\n }\n}\n\nfunction drawOutlineStroke(ctx, data) {\n if (!data || !data.lineStrips || !data.vertices) return\n \n const vertices = data.vertices\n const lineStrips = data.lineStrips\n \n let largestStrip = null\n let largestArea = 0\n \n for (const strip of lineStrips) {\n if (strip.count < 3) continue\n \n let ptr = strip.start * 3\n const startX = vertices[ptr]\n const startY = vertices[ptr + 1]\n const lastPtr = (strip.start + strip.count - 1) * 3\n const lastX = vertices[lastPtr]\n const lastY = vertices[lastPtr + 1]\n const dist = Math.sqrt(Math.pow(startX - lastX, 2) + Math.pow(startY - lastY, 2))\n \n if (dist < 0.5) {\n let area = 0\n for (let i = 0; i < strip.count; i++) {\n const p1 = (strip.start + i) * 3\n const p2 = (strip.start + (i + 1) % strip.count) * 3\n area += vertices[p1] * vertices[p2 + 1]\n area -= vertices[p2] * vertices[p1 + 1]\n }\n area = Math.abs(area) / 2\n \n if (area > largestArea) {\n largestArea = area\n largestStrip = strip\n }\n }\n }\n \n if (!largestStrip) return\n \n ctx.beginPath()\n let ptr = largestStrip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n \n for (let i = 1; i < largestStrip.count; i++) {\n ptr = (largestStrip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n ctx.closePath()\n ctx.stroke()\n}\n\nfunction drawOutlineFillAllClosed(ctx, data) {\n if (!data || !data.lineStrips || !data.vertices) return\n \n const vertices = data.vertices\n const lineStrips = data.lineStrips\n \n const closedPaths = []\n \n for (const strip of lineStrips) {\n if (strip.count < 3) continue\n \n let ptr = strip.start * 3\n const startX = vertices[ptr]\n const startY = vertices[ptr + 1]\n const lastPtr = (strip.start + strip.count - 1) * 3\n const lastX = vertices[lastPtr]\n const lastY = vertices[lastPtr + 1]\n const dist = Math.sqrt(Math.pow(startX - lastX, 2) + Math.pow(startY - lastY, 2))\n \n if (dist < 1.0) {\n // 计算面积和边界框\n let area = 0\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (let i = 0; i < strip.count; i++) {\n const p1 = (strip.start + i) * 3\n const p2 = (strip.start + (i + 1) % strip.count) * 3\n area += vertices[p1] * vertices[p2 + 1]\n area -= vertices[p2] * vertices[p1 + 1]\n \n const x = vertices[p1]\n const y = vertices[p1 + 1]\n if (x < minX) minX = x\n if (x > maxX) maxX = x\n if (y < minY) minY = y\n if (y > maxY) maxY = y\n }\n area = Math.abs(area) / 2\n \n closedPaths.push({ strip, area, bbox: { minX, minY, maxX, maxY } })\n }\n }\n \n // 对于 GKO 层,如果没有找到有效的闭合填充区域,\n // 尝试直接使用所有 lineStrips 作为路径填充\n // 因为 GKO 层通常是用线条(stroke)绘制的,每个 lineStrip 是线条的轮廓\n \n // 检查面积最大的几个区域是否都是极细的条形(线条轮廓)\n const sortedByAreaForCheck = [...closedPaths].sort((a, b) => b.area - a.area)\n const topPaths = sortedByAreaForCheck.slice(0, Math.min(10, sortedByAreaForCheck.length))\n const strokeOutlineCount = topPaths.filter(path => {\n const w = path.bbox.maxX - path.bbox.minX\n const h = path.bbox.maxY - path.bbox.minY\n const aspectRatio = Math.max(w / h, h / w)\n return aspectRatio > 50 // 宽高比 > 50:1 的都是线条轮廓\n }).length\n \n // 如果前 10 个最大区域中有 80% 以上是线条轮廓,就认为是线条轮廓模式\n const isStrokeOutlineMode = closedPaths.length > 0 && strokeOutlineCount >= topPaths.length * 0.8\n \n if (closedPaths.length === 0 || isStrokeOutlineMode) {\n if (DEBUG) console.log(`[仿真图] GKO层: 检测到线条轮廓模式,使用参考边界作为矩形轮廓`)\n \n // GKO 层是用线条绘制的,数据格式不适合提取异形轮廓\n // 直接使用 refBounds 作为矩形轮廓\n if (refBounds) {\n const refWidth = refBounds.maxX - refBounds.minX\n const refHeight = refBounds.maxY - refBounds.minY\n if (DEBUG) console.log(`[仿真图] GKO层: 矩形轮廓 ${refWidth.toFixed(2)} x ${refHeight.toFixed(2)}`)\n ctx.beginPath()\n ctx.rect(refBounds.minX, refBounds.minY, refWidth, refHeight)\n ctx.fill()\n }\n return\n }\n \n // 根据参考边界计算动态阈值\n let minDimThreshold = 0.001 // 默认最小尺寸\n let minAreaThreshold = 0.0001 // 默认最小面积\n \n if (refBounds) {\n const refWidth = refBounds.maxX - refBounds.minX\n const refHeight = refBounds.maxY - refBounds.minY\n // 最小尺寸:参考边界短边的 0.5%(更宽松,支持小安装孔)\n minDimThreshold = Math.min(refWidth, refHeight) * 0.005\n // 最小面积:参考边界面积的 0.1%(更宽松,支持小安装孔 ~2-3mm 直径)\n minAreaThreshold = refWidth * refHeight * 0.001\n }\n \n // 分析所有闭合路径,找出最大的几个\n const sortedByArea = [...closedPaths].sort((a, b) => b.area - a.area)\n if (sortedByArea.length > 0) {\n const top5 = sortedByArea.slice(0, 5)\n if (DEBUG) console.log(`[仿真图] GKO层: 面积最大的 5 个区域:`)\n top5.forEach((p, i) => {\n const w = p.bbox.maxX - p.bbox.minX\n const h = p.bbox.maxY - p.bbox.minY\n const ar = Math.max(w / h, h / w)\n if (DEBUG) console.log(` ${i + 1}. 面积=${p.area.toFixed(4)}, 尺寸=${w.toFixed(3)} x ${h.toFixed(3)}, 宽高比=${ar.toFixed(1)}`)\n })\n }\n \n // 过滤掉形状异常的区域(如细长的线条、太小的区域)\n const validShapePaths = closedPaths.filter(path => {\n const pathWidth = path.bbox.maxX - path.bbox.minX\n const pathHeight = path.bbox.maxY - path.bbox.minY\n const pathArea = path.area\n \n // 排除宽度或高度接近 0 的区域\n if (pathWidth < minDimThreshold || pathHeight < minDimThreshold) return false\n \n // 排除面积太小的区域\n if (pathArea < minAreaThreshold) return false\n \n // 排除宽高比异常的区域(> 10:1)\n const aspectRatio = Math.max(pathWidth / pathHeight, pathHeight / pathWidth)\n if (aspectRatio > 10) return false\n \n return true\n })\n \n if (DEBUG) console.log(`[仿真图] GKO层: 形状过滤后 ${closedPaths.length} → ${validShapePaths.length} 个有效区域 (阈值: 尺寸>${minDimThreshold.toFixed(4)}, 面积>${minAreaThreshold.toFixed(4)})`)\n \n // 选择最匹配的轮廓:优先选择 **包含** refBounds 的轮廓(异形轮廓可能比 refBounds 大很多)\n let candidatePaths = validShapePaths\n \n if (refBounds && validShapePaths.length > 0) {\n const refWidth = refBounds.maxX - refBounds.minX\n const refHeight = refBounds.maxY - refBounds.minY\n const refArea = refWidth * refHeight\n const refCenterX = (refBounds.minX + refBounds.maxX) / 2\n const refCenterY = (refBounds.minY + refBounds.maxY) / 2\n \n if (DEBUG) console.log(`[仿真图] GKO层: 参考边界 = ${refWidth.toFixed(2)} x ${refHeight.toFixed(2)}, 中心 = (${refCenterX.toFixed(2)}, ${refCenterY.toFixed(2)})`)\n \n // 策略1:找到 **包含** refBounds 的轮廓(异形轮廓应该完全包含铜层区域)\n const containingPaths = validShapePaths.filter(path => {\n const bbox = path.bbox\n // 检查这个路径是否包含 refBounds(允许小的误差)\n const margin = Math.max(refWidth, refHeight) * 0.1 // 10% 容差\n const containsRef = bbox.minX <= refBounds.minX + margin &&\n bbox.maxX >= refBounds.maxX - margin &&\n bbox.minY <= refBounds.minY + margin &&\n bbox.maxY >= refBounds.maxY - margin\n return containsRef\n })\n \n if (containingPaths.length > 0) {\n if (DEBUG) console.log(`[仿真图] GKO层: 找到 ${containingPaths.length} 个包含参考边界的轮廓`)\n candidatePaths = containingPaths\n } else {\n // 策略2:如果没有包含 refBounds 的轮廓,找与 refBounds 有交集且尺寸合理的轮廓\n const overlappingPaths = validShapePaths.filter(path => {\n const bbox = path.bbox\n const pathWidth = bbox.maxX - bbox.minX\n const pathHeight = bbox.maxY - bbox.minY\n const pathArea = pathWidth * pathHeight\n \n // 检查是否与 refBounds 有交集\n const hasOverlap = !(bbox.maxX < refBounds.minX || bbox.minX > refBounds.maxX ||\n bbox.maxY < refBounds.minY || bbox.minY > refBounds.maxY)\n \n // 尺寸限制:面积至少是 refBounds 的 30%(排除太小的标注)\n const isReasonableSize = pathArea >= refArea * 0.3\n \n return hasOverlap && isReasonableSize\n })\n \n if (overlappingPaths.length > 0) {\n if (DEBUG) console.log(`[仿真图] GKO层: 找到 ${overlappingPaths.length} 个与参考边界重叠的轮廓`)\n candidatePaths = overlappingPaths\n } else {\n if (DEBUG) console.log(`[仿真图] GKO层: 过滤后无有效区域,使用所有 ${validShapePaths.length} 个有效形状区域`)\n candidatePaths = validShapePaths\n }\n }\n }\n \n let mainOutline = null\n let useRefBoundsAsOutline = false\n \n if (candidatePaths.length > 1) {\n // 对于异形轮廓:选择 **面积最大** 的轮廓作为主轮廓\n // 因为主轮廓应该包含所有的铜层区域,所以它应该是最大的\n candidatePaths.sort((a, b) => b.area - a.area)\n mainOutline = candidatePaths[0]\n const selectedWidth = mainOutline.bbox.maxX - mainOutline.bbox.minX\n const selectedHeight = mainOutline.bbox.maxY - mainOutline.bbox.minY\n if (DEBUG) console.log(`[仿真图] GKO层选择: 从 ${candidatePaths.length} 个候选区域中,选择面积最大的 ${selectedWidth.toFixed(2)} x ${selectedHeight.toFixed(2)} 的区域 (面积=${mainOutline.area.toFixed(4)})`)\n } else if (candidatePaths.length === 1) {\n mainOutline = candidatePaths[0]\n const selectedWidth = mainOutline.bbox.maxX - mainOutline.bbox.minX\n const selectedHeight = mainOutline.bbox.maxY - mainOutline.bbox.minY\n if (DEBUG) console.log(`[仿真图] GKO层选择: 只有 1 个候选区域,尺寸 = ${selectedWidth.toFixed(2)} x ${selectedHeight.toFixed(2)}`)\n } else if (refBounds) {\n // 没有候选路径,使用 refBounds 作为矩形轮廓\n const refWidth = refBounds.maxX - refBounds.minX\n const refHeight = refBounds.maxY - refBounds.minY\n if (DEBUG) console.log(`[仿真图] GKO层选择: 无有效轮廓,使用参考边界作为矩形轮廓 ${refWidth.toFixed(2)} x ${refHeight.toFixed(2)}`)\n useRefBoundsAsOutline = true\n // 绘制矩形轮廓\n ctx.beginPath()\n ctx.rect(refBounds.minX, refBounds.minY, refWidth, refHeight)\n ctx.fill()\n return\n } else {\n // 没有候选路径也没有 refBounds,回退到所有闭合路径中面积最大的\n closedPaths.sort((a, b) => b.area - a.area)\n mainOutline = closedPaths[0]\n const selectedWidth = mainOutline.bbox.maxX - mainOutline.bbox.minX\n const selectedHeight = mainOutline.bbox.maxY - mainOutline.bbox.minY\n if (DEBUG) console.log(`[仿真图] GKO层选择: 无候选区域,选择面积最大的区域 ${selectedWidth.toFixed(2)} x ${selectedHeight.toFixed(2)}`)\n }\n \n // 异形轮廓渲染:使用 evenodd 填充规则\n // 1. 主轮廓(外边界)\n // 2. 在主轮廓内部的所有闭合区域(铣槽/开口)会被挖空\n \n const mainBbox = mainOutline.bbox\n \n // 找出所有在主轮廓边界内部的闭合区域(这些是铣槽/开口)\n const innerPaths = candidatePaths.filter(path => {\n if (path === mainOutline) return false\n \n // 检查这个路径是否完全在主轮廓的边界框内\n const bbox = path.bbox\n const isInside = bbox.minX >= mainBbox.minX - 0.1 && \n bbox.maxX <= mainBbox.maxX + 0.1 &&\n bbox.minY >= mainBbox.minY - 0.1 && \n bbox.maxY <= mainBbox.maxY + 0.1\n \n // 还要排除太小的路径(可能是标注/噪点)\n const pathWidth = bbox.maxX - bbox.minX\n const pathHeight = bbox.maxY - bbox.minY\n const mainWidth = mainBbox.maxX - mainBbox.minX\n const mainHeight = mainBbox.maxY - mainBbox.minY\n // 最小尺寸:至少 1mm 或主轮廓尺寸的 0.5%(支持小安装孔)\n const minSize = Math.max(1.0, Math.min(mainWidth, mainHeight) * 0.005)\n const isReasonableSize = pathWidth > minSize && pathHeight > minSize\n \n return isInside && isReasonableSize\n })\n \n if (DEBUG) console.log(`[仿真图] GKO层异形: 主轮廓 + ${innerPaths.length} 个内部开口/铣槽`)\n \n // 使用 Path2D 和 evenodd 填充规则来渲染异形轮廓\n ctx.beginPath()\n \n // 添加主轮廓(外边界)\n let ptr = mainOutline.strip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n for (let i = 1; i < mainOutline.strip.count; i++) {\n ptr = (mainOutline.strip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n ctx.closePath()\n \n // 添加所有内部开口/铣槽(这些会被 evenodd 规则挖空)\n for (const innerPath of innerPaths) {\n ptr = innerPath.strip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n for (let i = 1; i < innerPath.strip.count; i++) {\n ptr = (innerPath.strip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n ctx.closePath()\n }\n \n // 使用 evenodd 填充规则:嵌套的路径会自动形成挖空效果\n ctx.fill('evenodd')\n}\n\nfunction drawOutlineFill(ctx, data) {\n drawOutlineFillAllClosed(ctx, data)\n}\n\nfunction drawDrillHoles(ctx, data) {\n if (!data) return\n \n const vertices = data.vertices\n const lineStrips = data.lineStrips\n \n if (!lineStrips || !vertices) {\n if (DEBUG) console.log('[仿真图] 钻孔数据缺失 lineStrips 或 vertices')\n return\n }\n \n // 性能优化:预先分类所有钻孔,然后批量绘制\n // 保持与原始逻辑完全相同的判断条件\n const circles = [] // {cx, cy, r}\n const slots = [] // {strip}\n let skippedCount = 0\n \n for (const strip of lineStrips) {\n if (strip.count < 8) {\n skippedCount++\n continue\n }\n \n // 计算边界框\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (let i = 0; i < strip.count; i++) {\n const ptr = (strip.start + i) * 3\n minX = Math.min(minX, vertices[ptr])\n minY = Math.min(minY, vertices[ptr + 1])\n maxX = Math.max(maxX, vertices[ptr])\n maxY = Math.max(maxY, vertices[ptr + 1])\n }\n const bboxWidth = maxX - minX\n const bboxHeight = maxY - minY\n const aspectRatio = Math.max(bboxWidth, bboxHeight) / Math.max(0.001, Math.min(bboxWidth, bboxHeight))\n \n const cx = (minX + maxX) / 2\n const cy = (minY + maxY) / 2\n \n // 判断是否为槽孔(宽高比 > 1.3)\n if (aspectRatio > 1.3) {\n slots.push({ strip })\n } else {\n // 圆孔:计算平均半径\n let sumR = 0\n for (let i = 0; i < strip.count; i++) {\n const ptr = (strip.start + i) * 3\n const dx = vertices[ptr] - cx\n const dy = vertices[ptr + 1] - cy\n sumR += Math.sqrt(dx * dx + dy * dy)\n }\n const avgR = sumR / strip.count\n if (avgR > 0.001) {\n circles.push({ cx, cy, r: avgR })\n } else {\n skippedCount++\n }\n }\n }\n \n // 批量绘制圆孔(一次 beginPath + 多个 arc + 一次 fill)\n if (circles.length > 0) {\n ctx.beginPath()\n for (const c of circles) {\n ctx.moveTo(c.cx + c.r, c.cy)\n ctx.arc(c.cx, c.cy, c.r, 0, Math.PI * 2)\n }\n ctx.fill()\n }\n \n // 批量绘制槽孔\n if (slots.length > 0) {\n ctx.beginPath()\n for (const s of slots) {\n const strip = s.strip\n let ptr = strip.start * 3\n ctx.moveTo(vertices[ptr], vertices[ptr + 1])\n for (let i = 1; i < strip.count; i++) {\n ptr = (strip.start + i) * 3\n ctx.lineTo(vertices[ptr], vertices[ptr + 1])\n }\n ctx.closePath()\n }\n ctx.fill()\n }\n \n if (DEBUG) console.log(`[仿真图] 钻孔渲染完成: 绘制 ${circles.length + slots.length} 个 (圆孔=${circles.length}, 槽孔=${slots.length}), 跳过 ${skippedCount} 个`)\n}\n\n// 区域聚类(简化的 DBSCAN)\nfunction clusterRegions(regions, distThreshold) {\n if (regions.length === 0) return []\n \n const clusters = []\n const visited = new Set()\n \n for (let i = 0; i < regions.length; i++) {\n if (visited.has(i)) continue\n \n // 开始一个新簇\n const cluster = [regions[i]]\n visited.add(i)\n \n // BFS 查找所有相邻区域\n const queue = [i]\n while (queue.length > 0) {\n const current = queue.shift()\n const currentRegion = regions[current]\n \n for (let j = 0; j < regions.length; j++) {\n if (visited.has(j)) continue\n \n const other = regions[j]\n \n // 检查两个区域是否相邻(边界框距离小于阈值)\n const dx = Math.max(0, Math.max(currentRegion.minX - other.maxX, other.minX - currentRegion.maxX))\n const dy = Math.max(0, Math.max(currentRegion.minY - other.maxY, other.minY - currentRegion.maxY))\n const dist = Math.sqrt(dx * dx + dy * dy)\n \n if (dist < distThreshold) {\n visited.add(j)\n cluster.push(regions[j])\n queue.push(j)\n }\n }\n }\n \n clusters.push(cluster)\n }\n \n return clusters\n}\n\n// 计算点集的凸包(Graham Scan 算法)\nfunction computeConvexHull(points) {\n if (points.length < 3) return points\n \n // 找到最下面的点(y 最小,如果相同则取 x 最小)\n let start = 0\n for (let i = 1; i < points.length; i++) {\n if (points[i].y < points[start].y || \n (points[i].y === points[start].y && points[i].x < points[start].x)) {\n start = i\n }\n }\n \n // 把起始点放到第一个位置\n const temp = points[0]\n points[0] = points[start]\n points[start] = temp\n \n const pivot = points[0]\n \n // 按极角排序\n const sorted = points.slice(1).sort((a, b) => {\n const angleA = Math.atan2(a.y - pivot.y, a.x - pivot.x)\n const angleB = Math.atan2(b.y - pivot.y, b.x - pivot.x)\n if (angleA !== angleB) return angleA - angleB\n // 如果角度相同,按距离排序\n const distA = (a.x - pivot.x) ** 2 + (a.y - pivot.y) ** 2\n const distB = (b.x - pivot.x) ** 2 + (b.y - pivot.y) ** 2\n return distA - distB\n })\n \n // 叉积:判断三点的转向\n const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)\n \n // 构建凸包\n const hull = [pivot]\n for (const point of sorted) {\n while (hull.length > 1 && cross(hull[hull.length - 2], hull[hull.length - 1], point) <= 0) {\n hull.pop()\n }\n hull.push(point)\n }\n \n return hull\n}\n\nfunction hexToRgba(hex, alpha) {\n const r = parseInt(hex.slice(1, 3), 16)\n const g = parseInt(hex.slice(3, 5), 16)\n const b = parseInt(hex.slice(5, 7), 16)\n return `rgba(${r}, ${g}, ${b}, ${alpha})`\n}\n\n// ==================== UI 辅助函数 ====================\n\nfunction setView(view) {\n currentView = view\n document.getElementById('btnTopView').classList.toggle('active', view === 'top')\n document.getElementById('btnBottomView').classList.toggle('active', view === 'bottom')\n panOffset = { x: 0, y: 0 }\n render()\n}\n\nfunction fitToView() {\n zoomLevel = 1\n panOffset = { x: 0, y: 0 }\n}\n\nfunction showLoading(text) {\n loadingText.textContent = text\n loadingOverlay.classList.remove('hidden')\n}\n\nfunction hideLoading() {\n loadingOverlay.classList.add('hidden')\n}\n\nfunction updateStatus(text, isError = false) {\n if (statusLeft) {\n statusLeft.textContent = text\n }\n \n // 同时输出到控制台(方便调试和性能监控)\n if (isError) {\n console.error(`[simulation] 错误: ${text}`)\n } else if (text.includes('%')) {\n // 下载进度日志(包含百分比的)\n console.log(`[simulation] 状态: ${text}`)\n } else {\n console.log(`[simulation] 状态: ${text}`)\n }\n}\n\nfunction updateLayerList() {\n // 图层列表已隐藏\n}\n\nfunction clearAll() {\n layers = {}\n boardBounds = null\n refBounds = null\n zoomLevel = 1\n panOffset = { x: 0, y: 0 }\n invalidateCache()\n render()\n updateLayerList()\n updateStatus('就绪')\n}\n\nfunction exportImage() {\n if (!canvas || !boardBounds) {\n alert('请先加载 Gerber 文件')\n return\n }\n \n const link = document.createElement('a')\n link.download = `pcb_simulation_${currentView}.png`\n link.href = canvas.toDataURL('image/png')\n link.click()\n}\n\n// 导出给外部使用\nwindow.simulationViewer = {\n loadFromId,\n loadFromUrl,\n render,\n setView,\n config,\n handleFiles, // 导出文件处理函数,供 render 页面调用\n initSimulation // 导出初始化函数\n}\n\n// 兼容旧的调用方式\nwindow.simulationHandleFiles = handleFiles\n\n"],"mappings":"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","names":["JSZip","ctx","boardBounds","ctx","ptr","ctx"]}