@yumiai/chat-widget 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +1 -1
  3. package/dist/ExcelCore-EAXQMKZT.js +10 -0
  4. package/dist/{ExcelViewer-3YLLYYIQ.js → ExcelViewer-OGWM52ZT.js} +2 -3
  5. package/dist/{ExcelViewer-3YLLYYIQ.js.map → ExcelViewer-OGWM52ZT.js.map} +1 -1
  6. package/dist/{GerberViewerA2UI-X5FWAD5M.js → GerberViewerA2UI-AORHOCF6.js} +2 -3
  7. package/dist/{GerberViewerA2UI-X5FWAD5M.js.map → GerberViewerA2UI-AORHOCF6.js.map} +1 -1
  8. package/dist/{GraphStatsLegend-D5bPeXB_.d.cts → GraphStatsLegend-CV0Y3-cP.d.cts} +39 -0
  9. package/dist/{GraphStatsLegend-D5bPeXB_.d.ts → GraphStatsLegend-CV0Y3-cP.d.ts} +39 -0
  10. package/dist/JsonRenderStandalone-L5ROJ6XP.js +17 -0
  11. package/dist/{KicadViewer-GV6ZC4AQ.js → KicadViewer-H6YY6WVC.js} +3 -4
  12. package/dist/{KicadViewer-GV6ZC4AQ.js.map → KicadViewer-H6YY6WVC.js.map} +1 -1
  13. package/dist/KicadViewerCore-XVJE2FTQ.js +10 -0
  14. package/dist/{PdfViewer-CHPDRK46.js → PdfViewer-GKVDDNCZ.js} +9 -10
  15. package/dist/{PdfViewer-CHPDRK46.js.map → PdfViewer-GKVDDNCZ.js.map} +1 -1
  16. package/dist/{PdfViewerCore-HJPEHSRA.js → PdfViewerCore-SUVXHXYO.js} +24 -11
  17. package/dist/PdfViewerCore-SUVXHXYO.js.map +1 -0
  18. package/dist/PowerPointCore-IZ4G6HEQ.js +10 -0
  19. package/dist/{PowerPointViewer-LQTO6UCU.js → PowerPointViewer-5R2KSWWJ.js} +2 -3
  20. package/dist/{PowerPointViewer-LQTO6UCU.js.map → PowerPointViewer-5R2KSWWJ.js.map} +1 -1
  21. package/dist/{StepViewerCore-7W3L3R4E.js → StepViewerCore-RORWXIRU.js} +2 -3
  22. package/dist/{StepViewerCore-7W3L3R4E.js.map → StepViewerCore-RORWXIRU.js.map} +1 -1
  23. package/dist/{ThreeViewerCore-N3QJD5QI.js → ThreeViewerCore-GTUZKD5V.js} +2 -3
  24. package/dist/{ThreeViewerCore-N3QJD5QI.js.map → ThreeViewerCore-GTUZKD5V.js.map} +1 -1
  25. package/dist/WordCore-QFG5HTYD.js +10 -0
  26. package/dist/{WordViewer-ZHCQMHOH.js → WordViewer-7XUQFS4A.js} +2 -3
  27. package/dist/{WordViewer-ZHCQMHOH.js.map → WordViewer-7XUQFS4A.js.map} +1 -1
  28. package/dist/{chunk-QLVPIM6R.js → chunk-3CNQONCV.js} +79 -1
  29. package/dist/chunk-3CNQONCV.js.map +1 -0
  30. package/dist/{chunk-2UC7YLVX.js → chunk-3LOSHCSH.js} +3 -3
  31. package/dist/chunk-5VUKEGFC.js +108 -0
  32. package/dist/chunk-5VUKEGFC.js.map +1 -0
  33. package/dist/chunk-AO4YUJQT.js +479 -0
  34. package/dist/chunk-AO4YUJQT.js.map +1 -0
  35. package/dist/{chunk-KQV7IKET.js → chunk-ASV4TISB.js} +2 -2
  36. package/dist/{chunk-CFKGNAJM.js → chunk-AT6VLLOO.js} +15 -15
  37. package/dist/{chunk-56WRZM3R.js → chunk-EYWNUJVZ.js} +3 -3
  38. package/dist/{chunk-K4KGNVL5.js → chunk-EZ46FGQ6.js} +3 -3
  39. package/dist/{chunk-GYXTSY22.js → chunk-KXPR6SRW.js} +4 -4
  40. package/dist/{chunk-7S67DOHQ.js → chunk-MTN6SUUQ.js} +2 -2
  41. package/dist/chunk-Q5PLT3AI.js +29 -0
  42. package/dist/chunk-Q5PLT3AI.js.map +1 -0
  43. package/dist/{chunk-PZXSASDY.js → chunk-R5LHMOAC.js} +3 -3
  44. package/dist/chunk-X67V7257.js +238 -0
  45. package/dist/chunk-X67V7257.js.map +1 -0
  46. package/dist/citationContext-CILHTO2Z.js +25 -0
  47. package/dist/components/JsonRender/standalone.cjs +56 -9753
  48. package/dist/components/JsonRender/standalone.cjs.map +1 -1
  49. package/dist/components/JsonRender/standalone.js +10 -11
  50. package/dist/components/JsonRender/standalone.js.map +1 -1
  51. package/dist/{gerber-2d-entry-OQ4SQRBY.js → gerber-2d-entry-HEFXQGBK.js} +5 -9
  52. package/dist/{gerber-2d-entry-OQ4SQRBY.js.map → gerber-2d-entry-HEFXQGBK.js.map} +1 -1
  53. package/dist/{gerber-3d-entry-DEHDBOO2.js → gerber-3d-entry-KVTONA37.js} +5 -9
  54. package/dist/{gerber-3d-entry-DEHDBOO2.js.map → gerber-3d-entry-KVTONA37.js.map} +1 -1
  55. package/dist/{gerber-simulation-entry-EBDX72XE.js → gerber-simulation-entry-GZ62QX5H.js} +5 -9
  56. package/dist/{gerber-simulation-entry-EBDX72XE.js.map → gerber-simulation-entry-GZ62QX5H.js.map} +1 -1
  57. package/dist/index.cjs +6690 -13472
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.css +776 -4
  60. package/dist/index.css.map +1 -1
  61. package/dist/index.d.cts +979 -26
  62. package/dist/index.d.ts +979 -26
  63. package/dist/index.js +4350 -1859
  64. package/dist/index.js.map +1 -1
  65. package/dist/provenance/index.cjs +78 -0
  66. package/dist/provenance/index.cjs.map +1 -1
  67. package/dist/provenance/index.d.cts +2 -2
  68. package/dist/provenance/index.d.ts +2 -2
  69. package/dist/provenance/index.js +2 -3
  70. package/dist/{resolveToArrayBuffer-AQIDZHSQ.js → resolveToArrayBuffer-PVSVIAII.js} +1 -3
  71. package/dist/{resolveToArrayBuffer-AQIDZHSQ.js.map → resolveToArrayBuffer-PVSVIAII.js.map} +1 -1
  72. package/dist/sseAdapter-LFXYGYC4.js +8 -0
  73. package/dist/sseAdapter-LFXYGYC4.js.map +1 -0
  74. package/package.json +4 -1
  75. package/dist/ExcelCore-DJOIVQMI.js +0 -11
  76. package/dist/JsonRenderStandalone-EIZM62JU.js +0 -18
  77. package/dist/KicadViewerCore-U7BWZHKJ.js +0 -11
  78. package/dist/PdfViewerCore-HJPEHSRA.js.map +0 -1
  79. package/dist/PowerPointCore-FPDR2BL4.js +0 -11
  80. package/dist/WordCore-JKSXK2XD.js +0 -11
  81. package/dist/chunk-7A4FY6FK.js +0 -10226
  82. package/dist/chunk-7A4FY6FK.js.map +0 -1
  83. package/dist/chunk-7D4SUZUM.js +0 -38
  84. package/dist/chunk-QLVPIM6R.js.map +0 -1
  85. package/dist/chunk-VXJWGLZ7.js +0 -21
  86. package/dist/chunk-VXJWGLZ7.js.map +0 -1
  87. /package/dist/{ExcelCore-DJOIVQMI.js.map → ExcelCore-EAXQMKZT.js.map} +0 -0
  88. /package/dist/{JsonRenderStandalone-EIZM62JU.js.map → JsonRenderStandalone-L5ROJ6XP.js.map} +0 -0
  89. /package/dist/{KicadViewerCore-U7BWZHKJ.js.map → KicadViewerCore-XVJE2FTQ.js.map} +0 -0
  90. /package/dist/{PowerPointCore-FPDR2BL4.js.map → PowerPointCore-IZ4G6HEQ.js.map} +0 -0
  91. /package/dist/{WordCore-JKSXK2XD.js.map → WordCore-QFG5HTYD.js.map} +0 -0
  92. /package/dist/{chunk-2UC7YLVX.js.map → chunk-3LOSHCSH.js.map} +0 -0
  93. /package/dist/{chunk-KQV7IKET.js.map → chunk-ASV4TISB.js.map} +0 -0
  94. /package/dist/{chunk-CFKGNAJM.js.map → chunk-AT6VLLOO.js.map} +0 -0
  95. /package/dist/{chunk-56WRZM3R.js.map → chunk-EYWNUJVZ.js.map} +0 -0
  96. /package/dist/{chunk-K4KGNVL5.js.map → chunk-EZ46FGQ6.js.map} +0 -0
  97. /package/dist/{chunk-GYXTSY22.js.map → chunk-KXPR6SRW.js.map} +0 -0
  98. /package/dist/{chunk-7S67DOHQ.js.map → chunk-MTN6SUUQ.js.map} +0 -0
  99. /package/dist/{chunk-PZXSASDY.js.map → chunk-R5LHMOAC.js.map} +0 -0
  100. /package/dist/{chunk-7D4SUZUM.js.map → citationContext-CILHTO2Z.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/jetPaveGerberViewer/src/viewer-src/2dPage/js/gerber-parser-direct.js","../src/components/jetPaveGerberViewer/src/viewer-src/2dPage/js/main.js"],"sourcesContent":["/**\n * 直接解析 Gerber 文件并转换为 WebGL 几何数据\n * 不依赖 web-gerber,直接处理 G-code\n */\nimport earcut from 'earcut'\n\nexport class GerberDirectParser {\n constructor(options = {}) {\n this.vertices = [] // x, y, z\n this.colors = [] // r, g, b\n this.indices = [] // for line outlines (optional)\n this.triangleIndices = [] // for solid fills\n this.features = [] // 保存特征点(圆形、矩形等),用于钻孔对齐\n \n // 配置选项\n this.minLineWidth = options.minLineWidth || 0\n\n // 状态机\n this.state = {\n x: 0,\n y: 0,\n i: 0,\n j: 0,\n d: 0, // D-code (1: draw, 2: move, 3: flash)\n g: 1, // G-code (1: linear, 2: cw arc, 3: ccw arc, 36/37: region)\n aperture: null, // 当前光圈 D-code (例如 10, 11...)\n interpolation: 'linear', // linear, cw, ccw\n regionMode: false, // G36/G37\n quadrant: 'multi', // 'single' or 'multi' (G74/G75)\n unit: 'mm', // 'mm' or 'inch'\n polarity: 'dark', // 'dark' (正极性,填充) 或 'clear' (负极性,挖空)\n format: {\n integer: 2,\n decimal: 4,\n zero: 'L' // 'L'eading or 'T'railing\n }\n }\n\n // 极性段落(按 Gerber 命令顺序)。用于渲染阶段做 stencil:dark 写 1,clear 写 0。\n // 记录的是 triangleIndices 的区间 [start, end),所以不需要额外拆分 buffer。\n this.polarityRuns = [{ polarity: this.state.polarity, start: 0, end: 0 }]\n\n // 光圈定义字典\n this.apertures = {}\n // 宏定义字典\n this.macros = {}\n \n // 区域路径缓存(Gerber Region: G36/G37)\n // 一个 Region 里可能包含多个轮廓(D02 分段),并且存在孔洞。\n this.currentRegionContours = [] // Array<Array<{x:number,y:number}>>\n this._currentRegionContour = null\n }\n\n parse(content) {\n const lines = content.split(/[\\n\\r]+/)\n \n for (let line of lines) {\n line = line.trim()\n if (!line) continue\n \n // 处理扩展命令 (%)\n if (line.startsWith('%') && line.endsWith('%')) {\n this.parseExtendedCommand(line.substring(1, line.length - 1))\n continue\n }\n \n // 处理基本命令 (G, D, M, X, Y)\n // 基本命令可能在一行中有多个,以 * 结尾\n const commands = line.split('*')\n for (let cmd of commands) {\n cmd = cmd.trim()\n if (!cmd) continue\n this.parseCommand(cmd)\n }\n }\n\n // 结束最后一个 polarity run\n if (this.polarityRuns.length > 0) {\n this.polarityRuns[this.polarityRuns.length - 1].end = this.triangleIndices.length\n }\n\n return {\n vertices: new Float32Array(this.vertices),\n colors: new Float32Array(this.colors),\n triangleIndices: new Uint32Array(this.triangleIndices), // 改用 Uint32Array 支持大索引\n features: this.features, // 返回特征点\n // 仅返回非空 run,避免产生大量空段\n polarityRuns: this.polarityRuns.filter(r => (r.end - r.start) > 0)\n }\n }\n\n parseExtendedCommand(cmd) {\n // 格式定义 FS\n if (cmd.startsWith('FS')) {\n // FSLAX24Y24\n const match = cmd.match(/FS([LT])?[AI]X(\\d)(\\d)Y(\\d)(\\d)/)\n if (match) {\n this.state.format.zero = match[1] || 'L'\n this.state.format.integer = parseInt(match[2])\n this.state.format.decimal = parseInt(match[3])\n }\n }\n // 单位定义 MO\n else if (cmd.startsWith('MO')) {\n this.state.unit = cmd.includes('IN') ? 'inch' : 'mm'\n }\n // 光圈定义 AD\n else if (cmd.startsWith('AD')) {\n // ADD10C,0.0100\n const match = cmd.match(/ADD(\\d+)([a-zA-Z]+),?(.*)/)\n if (match) {\n const dCode = parseInt(match[1])\n const type = match[2]\n const params = match[3].split('X').map(p => parseFloat(p))\n this.apertures[dCode] = { type, params }\n }\n }\n // 极性 LP(LPD: dark, LPC: clear)\n else if (cmd.startsWith('LP')) {\n if (cmd.startsWith('LPD')) this._switchPolarity('dark')\n else if (cmd.startsWith('LPC')) this._switchPolarity('clear')\n }\n // 宏定义 AM (暂略,复杂)\n // 注意:本项目当前主要依赖“Region 的多轮廓/孔洞”表达镂空(本文件无 LP)。\n // 若后续需要完整支持 LP(dark/clear)做布尔差集,应在渲染阶段按 polarity 做 stencil/布尔运算。\n }\n\n _switchPolarity(nextPolarity) {\n if (!nextPolarity || this.state.polarity === nextPolarity) return\n\n // 结束当前 run\n if (this.polarityRuns.length > 0) {\n this.polarityRuns[this.polarityRuns.length - 1].end = this.triangleIndices.length\n }\n\n this.state.polarity = nextPolarity\n\n // 开始新 run\n const start = this.triangleIndices.length\n this.polarityRuns.push({ polarity: nextPolarity, start, end: start })\n }\n\n parseCommand(cmd) {\n // G 命令\n if (cmd.startsWith('G')) {\n const gCode = parseInt(cmd.substring(1, 3))\n // G54:选择光圈(Aperture Select)。很多Gerber会用 G54D10* / G54D11* 切换光圈。\n // 之前我们把它当作普通G命令并走 parseCoordinate,导致 D10 被误当成“操作码”而不是“选择光圈”,\n // 从而 state.aperture 一直为 null,所有线段/焊盘都会被跳过(只剩下G36/G37区域能显示)。\n if (gCode === 54) {\n const dSel = cmd.match(/D(\\d+)/)\n if (dSel) {\n const dCode = parseInt(dSel[1], 10)\n if (dCode >= 10) {\n this.state.aperture = dCode\n return\n }\n }\n // 如果没有 D10+,则忽略\n return\n }\n if (gCode === 1) this.state.interpolation = 'linear'\n if (gCode === 2) this.state.interpolation = 'cw'\n if (gCode === 3) this.state.interpolation = 'ccw'\n if (gCode === 36) {\n this.state.regionMode = true\n this.currentRegionContours = []\n this._currentRegionContour = null\n }\n if (gCode === 37) {\n this.state.regionMode = false\n this.finishRegion()\n }\n \n // G命令后可能紧跟坐标\n if (cmd.length > 3) {\n this.parseCoordinate(cmd.substring(3))\n }\n return\n }\n\n // D 命令\n if (cmd.startsWith('D')) {\n const dCode = parseInt(cmd.substring(1))\n // D10+ 是选择光圈\n if (dCode >= 10) {\n this.state.aperture = dCode\n return\n }\n \n // D01/D02/D03 作为独立命令(没有坐标),使用当前状态位置执行操作\n if (dCode === 1 || dCode === 2 || dCode === 3) {\n // 使用当前状态的位置\n const x = this.state.x\n const y = this.state.y\n \n if (dCode === 1) { // Draw\n // 独立 D01,如果有光圈,绘制线段到当前点(相当于点)\n if (this.state.aperture) {\n // 这里通常应该结合上一个点画线,但如果原地不动,可能就是个点\n // 暂且当作 Flash 处理,或者忽略\n }\n } else if (dCode === 2) { // Move\n // 仅更新位置,无需操作\n } else if (dCode === 3) { // Flash\n this.addFlash(x, y)\n }\n return\n }\n }\n\n // 坐标操作 X...Y...D...\n this.parseCoordinate(cmd)\n }\n\n parseCoordinate(cmd) {\n let x = this.state.x\n let y = this.state.y\n let iOffset = 0\n let jOffset = 0\n let d = null\n\n // 解析 X\n const xMatch = cmd.match(/X([-+]?\\d+)/)\n if (xMatch) {\n x = this.parseValue(xMatch[1])\n }\n\n // 解析 Y\n const yMatch = cmd.match(/Y([-+]?\\d+)/)\n if (yMatch) {\n y = this.parseValue(yMatch[1])\n }\n \n // 解析 I (圆心偏移 X)\n const iMatch = cmd.match(/I([-+]?\\d+)/)\n if (iMatch) {\n iOffset = this.parseValue(iMatch[1])\n }\n\n // 解析 J (圆心偏移 Y)\n const jMatch = cmd.match(/J([-+]?\\d+)/)\n if (jMatch) {\n jOffset = this.parseValue(jMatch[1])\n }\n\n // 解析 D (1, 2, 3)\n const dMatch = cmd.match(/D(\\d+)/)\n if (dMatch) {\n d = parseInt(dMatch[1])\n }\n\n // 更新状态 (X, Y)\n const prevX = this.state.x\n const prevY = this.state.y\n this.state.x = x\n this.state.y = y\n\n // 执行操作\n if (d !== null) {\n if (d === 1) { // Draw / Expose\n if (this.state.regionMode) {\n // 区域模式\n if (this.state.interpolation === 'linear') {\n // 如果 region 里没有显式 D02 起笔,则使用“上一点”作为起点创建轮廓\n if (!this._currentRegionContour) {\n this._startRegionContour(prevX, prevY)\n }\n this._currentRegionContour.push({ x, y })\n } else {\n // 区域模式下的圆弧:需要离散化为点加入路径\n if (!this._currentRegionContour) {\n this._startRegionContour(prevX, prevY)\n }\n this.addArcToRegion(prevX, prevY, x, y, iOffset, jOffset, this.state.interpolation === 'cw')\n }\n } else {\n // 线性模式\n if (this.state.interpolation === 'linear') {\n this.addStroke(prevX, prevY, x, y)\n // Round Caps\n if (this.state.aperture && this.apertures[this.state.aperture]?.type === 'C') {\n let width = this.apertures[this.state.aperture].params[0]\n if (this.state.unit === 'inch') width *= 25.4\n if (width < this.minLineWidth) width = this.minLineWidth\n this.addCircle(prevX, prevY, width / 2)\n this.addCircle(x, y, width / 2)\n }\n } else {\n // 圆弧插值\n this.addArcStroke(prevX, prevY, x, y, iOffset, jOffset, this.state.interpolation === 'cw')\n }\n }\n } else if (d === 2) { // Move\n if (this.state.regionMode) {\n // Region 内 D02:开始一个新轮廓(用于孔洞/多多边形)\n this._startRegionContour(x, y)\n }\n // 仅更新位置\n } else if (d === 3) { // Flash\n this.addFlash(x, y)\n }\n }\n }\n\n _startRegionContour(x, y) {\n // 结束旧轮廓:不需要显式 close(earcut 不要求重复首点),后面会做去重/清洗\n const contour = [{ x, y }]\n this.currentRegionContours.push(contour)\n this._currentRegionContour = contour\n }\n \n // 添加圆弧绘制逻辑\n addArcStroke(x1, y1, x2, y2, iOffset, jOffset, isCw) {\n const cx = x1 + iOffset\n const cy = y1 + jOffset\n const radius = Math.sqrt(iOffset * iOffset + jOffset * jOffset)\n \n // [调试日志]\n // console.log(`[addArcStroke] Start:(${x1.toFixed(2)}, ${y1.toFixed(2)}) End:(${x2.toFixed(2)}, ${y2.toFixed(2)}) I=${iOffset.toFixed(2)} J=${jOffset.toFixed(2)} R=${radius.toFixed(2)} CW=${isCw}`)\n\n // 如果半径太小,忽略\n if (radius < 0.0001) {\n console.warn('Radius too small, skipping arc')\n return\n }\n \n // console.log(`[Arc] R=${radius.toFixed(3)} (${x1.toFixed(3)},${y1.toFixed(3)})->(${x2.toFixed(3)},${y2.toFixed(3)}) CW=${isCw}`)\n\n let startAngle = Math.atan2(y1 - cy, x1 - cx)\n let endAngle = Math.atan2(y2 - cy, x2 - cx)\n \n // 检测全圆 (Start ~= End)\n const dist = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))\n const isFullCircle = dist < 0.0001\n \n if (isCw) { // 顺时针\n if (isFullCircle) {\n endAngle = startAngle - 2 * Math.PI\n } else if (endAngle >= startAngle) {\n endAngle -= 2 * Math.PI\n }\n } else { // 逆时针\n if (isFullCircle) {\n endAngle = startAngle + 2 * Math.PI\n } else if (endAngle <= startAngle) {\n endAngle += 2 * Math.PI\n }\n }\n \n // 离散化\n // 估算弧长\n const arcLength = Math.abs(endAngle - startAngle) * radius\n // 根据弧长决定分段,精度控制在 0.1mm(降低精度提升性能)\n const segments = Math.ceil(arcLength / 0.1) \n const limitedSegments = Math.min(Math.max(segments, 8), 64) // 降低分段数:最小 8,最大 64\n \n // console.log(`[addArcStroke] Angles: ${startAngle.toFixed(2)} -> ${endAngle.toFixed(2)}, Segments: ${limitedSegments}`)\n\n let prevPx = x1\n let prevPy = y1\n \n // 只在圆弧起点加圆(端点会在循环结束后单独加)\n if (this.state.aperture && this.apertures[this.state.aperture]?.type === 'C') {\n let width = this.apertures[this.state.aperture].params[0]\n if (this.state.unit === 'inch') width *= 25.4\n if (width < this.minLineWidth) width = this.minLineWidth\n this.addCircle(x1, y1, width / 2)\n }\n \n for (let k = 1; k <= limitedSegments; k++) {\n const t = k / limitedSegments\n const angle = startAngle + (endAngle - startAngle) * t\n const px = cx + radius * Math.cos(angle)\n const py = cy + radius * Math.sin(angle)\n \n this.addStroke(prevPx, prevPy, px, py)\n \n // 不再在每个分段点加圆,避免过度遮挡\n \n prevPx = px\n prevPy = py\n }\n \n // 在圆弧终点加圆\n if (this.state.aperture && this.apertures[this.state.aperture]?.type === 'C') {\n let width = this.apertures[this.state.aperture].params[0]\n if (this.state.unit === 'inch') width *= 25.4\n if (width < this.minLineWidth) width = this.minLineWidth\n this.addCircle(x2, y2, width / 2)\n }\n }\n \n addArcToRegion(x1, y1, x2, y2, iOffset, jOffset, isCw) {\n const cx = x1 + iOffset\n const cy = y1 + jOffset\n const radius = Math.sqrt(iOffset * iOffset + jOffset * jOffset)\n \n if (radius < 0.0001) return\n\n let startAngle = Math.atan2(y1 - cy, x1 - cx)\n let endAngle = Math.atan2(y2 - cy, x2 - cx)\n \n const dist = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))\n const isFullCircle = dist < 0.0001\n \n if (isCw) {\n if (isFullCircle) {\n endAngle = startAngle - 2 * Math.PI\n } else if (endAngle >= startAngle) {\n endAngle -= 2 * Math.PI\n }\n } else {\n if (isFullCircle) {\n endAngle = startAngle + 2 * Math.PI\n } else if (endAngle <= startAngle) {\n endAngle += 2 * Math.PI\n }\n }\n \n const arcLength = Math.abs(endAngle - startAngle) * radius\n const segments = Math.max(8, Math.ceil(arcLength / 0.1)) // 降低精度以提升性能\n \n for (let k = 1; k <= segments; k++) {\n const t = k / segments\n const angle = startAngle + (endAngle - startAngle) * t\n if (this._currentRegionContour) {\n this._currentRegionContour.push({\n x: cx + radius * Math.cos(angle),\n y: cy + radius * Math.sin(angle),\n })\n }\n }\n }\n\n parseValue(str) {\n // 根据 FS 格式解析坐标值\n // 简单实现:假设 trailing zeros 省略 (常见的 Altium 输出)\n // 实际上应该根据 this.state.format.zero 和 integer/decimal 严谨解析\n \n // 假设是 mm, 小数点后4位 (常见配置)\n // 如果 str 包含小数点,直接解析\n if (str.includes('.')) return parseFloat(str)\n\n // 否则按定点数解析\n const totalLen = this.state.format.integer + this.state.format.decimal\n const sign = str.startsWith('-') || str.startsWith('+') ? str[0] : ''\n const numStr = sign ? str.substring(1) : str\n \n let val = 0\n // 这里简化处理,通用做法:\n // 如果是 Leading Zero Omission (省略前导零)\n // 需要补足位数,然后应用小数点\n \n // 让我们先用一个简单的 heuristic:通常 Gerber 单位是 mm 或 inch\n // 坐标值通常由 format 决定。\n // 比如 2:4 格式,'15000' -> 1.5000\n \n const scale = Math.pow(10, this.state.format.decimal)\n val = parseInt(str) / scale\n \n // 简单的单位转换到 WebGL 单位 (保持 mm 为基准)\n if (this.state.unit === 'inch') val *= 25.4\n \n return val\n }\n\n addStroke(x1, y1, x2, y2) {\n if (!this.state.aperture) {\n console.warn('[addStroke] No aperture selected')\n return\n }\n const ap = this.apertures[this.state.aperture]\n if (!ap) {\n console.warn(`[addStroke] Aperture D${this.state.aperture} not defined`)\n return\n }\n\n // 获取线宽\n let width = 0\n if (ap.type === 'C') width = ap.params[0] // Circle diameter\n if (ap.type === 'R') width = ap.params[0] // Rect width (approximation)\n \n if (this.state.unit === 'inch') width *= 25.4\n \n // 处理0宽度aperture(防止GG1等文件中的0宽度定义导致不渲染)\n if (width === 0) {\n console.warn(`[GerberDirect] Aperture D${this.state.aperture} 宽度为0,使用默认宽度 0.1mm`)\n width = 0.1\n }\n \n // 应用最小线宽\n if (width < this.minLineWidth) {\n width = this.minLineWidth\n }\n \n // 调试日志(仅对前几条线段)\n // if (!this._strokeCount) this._strokeCount = 0\n // if (this._strokeCount < 5) {\n // console.log(`[addStroke #${this._strokeCount}] (${x1.toFixed(2)},${y1.toFixed(2)})->(${x2.toFixed(2)},${y2.toFixed(2)}) Width=${width.toFixed(3)}mm Aperture=D${this.state.aperture}`)\n // }\n // this._strokeCount++\n\n // 三角化线段 (生成矩形)\n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx*dx + dy*dy)\n if (len === 0) return\n\n const ux = -dy / len * (width / 2)\n const uy = dx / len * (width / 2)\n\n // 四个角点\n const p1 = { x: x1 + ux, y: y1 + uy }\n const p2 = { x: x1 - ux, y: y1 - uy }\n const p3 = { x: x2 - ux, y: y2 - uy }\n const p4 = { x: x2 + ux, y: y2 + uy }\n\n this.addQuad(p1, p2, p3, p4)\n \n // 添加圆形接头(Round Cap)以填补连接处的缺口\n // 仅当光圈是圆形(Type C)时才添加圆形接头\n // 如果是矩形光圈绘制的线,通常应该是平头,但实际 Gerber 中很少用矩形光圈画线\n if (ap.type === 'C') {\n this.addCircle(x1, y1, width / 2)\n this.addCircle(x2, y2, width / 2)\n }\n }\n\n addFlash(x, y) {\n if (!this.state.aperture) return\n const ap = this.apertures[this.state.aperture]\n if (!ap) return\n\n if (ap.type === 'C') {\n // 圆形 Flash\n let r = ap.params[0] / 2\n if (this.state.unit === 'inch') r *= 25.4\n \n // 处理0半径aperture\n if (r === 0) {\n console.warn(`[GerberDirect] Aperture D${this.state.aperture} 半径为0,使用默认半径 0.05mm (直径0.1mm)`)\n r = 0.05\n }\n \n this.addCircle(x, y, r)\n \n // 记录特征点(圆形)\n this.features.push({\n type: 'circle',\n x, y, r\n })\n } else if (ap.type === 'R') { // 矩形\n let w = ap.params[0]\n let h = ap.params[1]\n if (this.state.unit === 'inch') { w *= 25.4; h *= 25.4 }\n \n const p1 = { x: x - w/2, y: y - h/2 }\n const p2 = { x: x + w/2, y: y - h/2 }\n const p3 = { x: x + w/2, y: y + h/2 }\n const p4 = { x: x - w/2, y: y + h/2 }\n this.addQuad(p1, p2, p3, p4)\n \n // 记录特征点(矩形)\n this.features.push({\n type: 'rect',\n x, y, w, h\n })\n } else if (ap.type === 'O') { // Obround(椭圆形/长圆形)\n let w = ap.params[0]\n let h = ap.params[1]\n if (this.state.unit === 'inch') { w *= 25.4; h *= 25.4 }\n \n // 渲染obround为两端半圆+中间矩形\n this.addObround(x, y, w, h)\n \n // 记录特征点(椭圆)\n this.features.push({\n type: 'obround',\n x, y, w, h\n })\n }\n }\n\n finishRegion() {\n const rawContours = this.currentRegionContours || []\n if (rawContours.length === 0) return\n\n // 清洗轮廓:去掉连续重复点、去掉闭合重复首点、去掉退化轮廓\n const cleanContour = (pts) => {\n if (!pts || pts.length === 0) return []\n const out = []\n let last = null\n for (const p of pts) {\n if (!last || Math.abs(p.x - last.x) > 1e-9 || Math.abs(p.y - last.y) > 1e-9) {\n out.push({ x: p.x, y: p.y })\n last = p\n }\n }\n if (out.length >= 2) {\n const first = out[0]\n const end = out[out.length - 1]\n if (Math.abs(first.x - end.x) < 1e-9 && Math.abs(first.y - end.y) < 1e-9) {\n out.pop()\n }\n }\n // 至少 3 个点\n if (out.length < 3) return []\n return out\n }\n\n const contours = rawContours.map(cleanContour).filter(c => c.length >= 3)\n if (contours.length === 0) return\n\n const signedArea = (pts) => {\n let a = 0\n for (let i = 0; i < pts.length; i++) {\n const p = pts[i]\n const q = pts[(i + 1) % pts.length]\n a += (p.x * q.y - q.x * p.y)\n }\n return a / 2\n }\n\n const bboxOf = (pts) => {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const p of pts) {\n minX = Math.min(minX, p.x)\n minY = Math.min(minY, p.y)\n maxX = Math.max(maxX, p.x)\n maxY = Math.max(maxY, p.y)\n }\n return { minX, minY, maxX, maxY }\n }\n\n const pointInPoly = (pt, poly) => {\n // Ray casting\n let inside = false\n for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {\n const xi = poly[i].x, yi = poly[i].y\n const xj = poly[j].x, yj = poly[j].y\n const intersect = ((yi > pt.y) !== (yj > pt.y)) &&\n (pt.x < (xj - xi) * (pt.y - yi) / ((yj - yi) || 1e-30) + xi)\n if (intersect) inside = !inside\n }\n return inside\n }\n\n // 构建包含关系(每个轮廓找最小的“父轮廓”)\n const metas = contours.map((pts, idx) => ({\n id: idx,\n pts,\n areaAbs: Math.abs(signedArea(pts)),\n bbox: bboxOf(pts),\n parent: -1,\n depth: 0,\n }))\n\n for (const a of metas) {\n const p = a.pts[0]\n let bestParent = null\n for (const b of metas) {\n if (a.id === b.id) continue\n // bbox 快速排除\n if (p.x < b.bbox.minX || p.x > b.bbox.maxX || p.y < b.bbox.minY || p.y > b.bbox.maxY) continue\n if (!pointInPoly(p, b.pts)) continue\n if (!bestParent || b.areaAbs < bestParent.areaAbs) {\n bestParent = b\n }\n }\n if (bestParent) a.parent = bestParent.id\n }\n\n // 计算深度\n const depthOf = (id) => {\n let d = 0\n let cur = metas[id]\n while (cur.parent !== -1) {\n d++\n cur = metas[cur.parent]\n }\n return d\n }\n for (const m of metas) m.depth = depthOf(m.id)\n\n // 每个 outer(深度为偶数)各自 triangulate,holes 取其直接子轮廓(深度=outer+1)\n const childrenMap = new Map()\n for (const m of metas) {\n if (!childrenMap.has(m.parent)) childrenMap.set(m.parent, [])\n childrenMap.get(m.parent).push(m.id)\n }\n\n const triangulateOne = (outerPts, holePtsList) => {\n const flat = []\n const holeIndices = []\n\n // outer\n for (const p of outerPts) flat.push(p.x, p.y)\n let cursor = outerPts.length\n\n // holes\n for (const holePts of holePtsList) {\n holeIndices.push(cursor)\n for (const p of holePts) flat.push(p.x, p.y)\n cursor += holePts.length\n }\n\n const triangles = earcut(flat, holeIndices.length ? holeIndices : null)\n if (!triangles || triangles.length === 0) return\n\n const baseIndex = this.vertices.length / 3\n for (let i = 0; i < flat.length; i += 2) {\n this.vertices.push(flat[i], flat[i + 1], 0)\n this.colors.push(1, 0, 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n this.triangleIndices.push(baseIndex + triangles[i])\n }\n }\n\n for (const m of metas) {\n if (m.depth % 2 !== 0) continue // holes 由 outer 处理\n const childIds = childrenMap.get(m.id) || []\n const directHoles = childIds\n .map(id => metas[id])\n .filter(c => c.depth === m.depth + 1) // 直接子层\n .map(c => c.pts)\n\n triangulateOne(m.pts, directHoles)\n }\n }\n\n addQuad(p1, p2, p3, p4) {\n const baseIndex = this.vertices.length / 3\n \n this.vertices.push(p1.x, p1.y, 0); this.colors.push(1, 0, 0)\n this.vertices.push(p2.x, p2.y, 0); this.colors.push(1, 0, 0)\n this.vertices.push(p3.x, p3.y, 0); this.colors.push(1, 0, 0)\n this.vertices.push(p4.x, p4.y, 0); this.colors.push(1, 0, 0)\n\n // 两个三角形组成矩形: 0-1-2, 0-2-3 (或者其他顺序,earcut顺时针)\n // p1(TL), p2(BL), p3(BR), p4(TR) -> assuming stroke direction logic\n // Let's use standard: 0,1,2 and 2,3,0\n this.triangleIndices.push(baseIndex, baseIndex+1, baseIndex+2)\n this.triangleIndices.push(baseIndex, baseIndex+2, baseIndex+3)\n }\n\n addCircle(cx, cy, r) {\n // 处理0半径\n if (r === 0) {\n r = 0.05 // 默认半径0.05mm\n }\n \n // 根据半径动态调整分段数(圆周长 / 0.2mm 作为分段数,保证足够圆滑)\n const circumference = 2 * Math.PI * r\n const segments = Math.min(Math.max(Math.ceil(circumference / 0.2), 12), 64)\n const baseIndex = this.vertices.length / 3\n \n this.vertices.push(cx, cy, 0) // Center\n this.colors.push(1, 0, 0)\n\n for (let i = 0; i <= segments; i++) {\n const theta = (i / segments) * Math.PI * 2\n this.vertices.push(cx + Math.cos(theta) * r, cy + Math.sin(theta) * r, 0)\n this.colors.push(1, 0, 0)\n }\n\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n }\n\n addObround(cx, cy, w, h) {\n // 处理0宽度/高度\n if (w === 0) w = 0.1\n if (h === 0) h = 0.1\n \n // Obround(椭圆形/长圆形)= 两端半圆 + 中间矩形\n if (w > h) {\n // 水平长圆形(宽度大于高度)\n const radius = h / 2\n const rectWidth = w - h // 中间矩形的宽度\n // 这里的 segments 是半圆的分段数\n const segments = Math.max(8, Math.ceil(radius * 4))\n \n // 左侧半圆中心\n const leftCx = cx - rectWidth / 2\n \n // 添加左半圆顶点 (PI/2 -> 3PI/2)\n let baseIndex = this.vertices.length / 3\n this.vertices.push(leftCx, cy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 90度 到 270度\n const angle = Math.PI/2 + (i / segments) * Math.PI\n this.vertices.push(leftCx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成左半圆三角形 (扇形)\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 中间矩形\n if (rectWidth > 0) {\n const p1 = { x: cx - rectWidth/2, y: cy - radius } // Top-Left\n const p2 = { x: cx - rectWidth/2, y: cy + radius } // Bottom-Left\n const p3 = { x: cx + rectWidth/2, y: cy + radius } // Bottom-Right\n const p4 = { x: cx + rectWidth/2, y: cy - radius } // Top-Right\n // 注意 addQuad 的参数顺序,需要确保是一个凸多边形顺序(如逆时针或顺时针)\n // 这里用: TL, BL, BR, TR\n this.addQuad(p1, p2, p3, p4)\n }\n \n // 右侧半圆中心\n const rightCx = cx + rectWidth / 2\n \n // 添加右半圆顶点 (-PI/2 -> PI/2)\n baseIndex = this.vertices.length / 3\n this.vertices.push(rightCx, cy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 -90度 到 90度\n const angle = -Math.PI/2 + (i / segments) * Math.PI\n this.vertices.push(rightCx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成右半圆三角形\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n } else {\n // 垂直长圆形(高度大于等于宽度)\n const radius = w / 2\n const rectHeight = h - w // 中间矩形的高度\n const segments = Math.max(8, Math.ceil(radius * 4))\n \n // 上侧半圆中心\n const topCy = cy + rectHeight / 2\n \n // 添加上半圆顶点 (0 -> PI)\n let baseIndex = this.vertices.length / 3\n this.vertices.push(cx, topCy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 0度 到 180度\n const angle = (i / segments) * Math.PI\n this.vertices.push(cx + Math.cos(angle) * radius, topCy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成上半圆三角形\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 中间矩形\n if (rectHeight > 0) {\n const p1 = { x: cx - radius, y: cy + rectHeight/2 } // Top-Left\n const p2 = { x: cx - radius, y: cy - rectHeight/2 } // Bottom-Left\n const p3 = { x: cx + radius, y: cy - rectHeight/2 } // Bottom-Right\n const p4 = { x: cx + radius, y: cy + rectHeight/2 } // Top-Right\n this.addQuad(p1, p2, p3, p4)\n }\n \n // 下侧半圆中心\n const bottomCy = cy - rectHeight / 2\n \n // 添加下半圆顶点 (PI -> 2PI)\n baseIndex = this.vertices.length / 3\n this.vertices.push(cx, bottomCy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 180度 到 360度\n const angle = Math.PI + (i / segments) * Math.PI\n this.vertices.push(cx + Math.cos(angle) * radius, bottomCy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成下半圆三角形\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n }\n }\n}\n","import { GerberDirectParser } from './gerber-parser-direct.js'\nimport { extractRar } from '../../common/archiveExtractor.js'\nimport JSZip from 'jszip'\nimport { parse, plot, renderSVG } from 'web-gerber'\nimport earcut from 'earcut'\nimport { initUrlIdHandler, parseUrlId, parseUrlFileUrl } from '../../common/gerber-common.js'\n\n\nconst canvas = document.getElementById('canvas')\nconst fileInput = document.getElementById('fileInput')\nconst status = document.getElementById('status')\nconst fileInfo = document.getElementById('fileInfo')\nconst fileName = document.getElementById('fileName')\nconst fileSize = document.getElementById('fileSize')\nconst layerList = document.getElementById('layerList')\nconst layerToggleAll = document.getElementById('layerToggleAll')\nconst layerToggleAllCheckbox = document.getElementById('layerToggleAllCheckbox')\nconst loadingOverlay = document.getElementById('loadingOverlay')\n\n// 显示加载遮罩\nfunction showLoading() {\n if (loadingOverlay) loadingOverlay.classList.add('active')\n}\n\n// 隐藏加载遮罩\nfunction hideLoading() {\n if (loadingOverlay) loadingOverlay.classList.remove('active')\n}\n\n// WebGL Context\nlet gl = canvas.getContext('webgl', { \n stencil: true, // 启用模版缓冲,用于处理层内重叠\n alpha: false,\n preserveDrawingBuffer: true\n})\nlet program = null\nlet positionLocation\nlet uColorLocation\nlet uAlphaLocation\nlet uMatrixLocation\nlet quadVertexBuffer = null\nlet quadIndexBuffer = null\nconst IDENTITY_MATRIX = [\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n]\n// let vertexBuffer, colorBuffer, indexBuffer\n\n// 图层数据\nlet layers = [] // 存储每一层的几何数据和状态\nlet scale = 1, transX = 0, transY = 0\nlet isDragging = false\nlet dragStartX = 0, dragStartY = 0\nlet dragStartTransX = 0, dragStartTransY = 0\n\n// 渲染缓存\n// let cachedVertices = null\n// let cachedColors = null\n// let cachedIndices = null\nlet cacheNeedsUpdate = true\n\n// 颜色表(十六进制)\nconst baseColors = [\n '#FF0000', '#14FF00', '#0014FF', '#FFFF14', '#FF930C',\n '#B2FF0C', '#3CB2FF', '#C9B20A', '#B2C932', '#6432C9',\n '#93B20A', '#B27C52', '#7EC755', '#C73C7E', '#3778C7',\n '#9B9B40', '#7A409B', '#407A9B', '#7A9B40', '#583778',\n]\n\n// 默认图层透明度\nconst defaultLayerAlpha = 1.0\n\n// 取颜色:超出颜色表则用白色\nfunction pickColor(idx) {\n return idx < baseColors.length ? baseColors[idx] : '#FFFFFF'\n}\n\n// Solo 模式相关\nlet layerColorMap = new Map() // 存储图层颜色映射\nlet isSoloMode = false // 是否处于单独显示模式\nlet soloLayerOrder = [] // 存储solo模式下按勾选顺序排列的图层索引\nlet soloTargetLayerIndex = null // 存储双击的目标图层索引\n\nif (!gl) {\n alert('WebGL not supported')\n} else {\n initWebGL()\n animate()\n}\n\nfunction initWebGL() {\n const vsSource = `\n attribute vec3 a_position;\n uniform mat4 u_matrix;\n void main() {\n gl_Position = u_matrix * vec4(a_position, 1.0);\n }\n `\n const fsSource = `\n precision mediump float;\n uniform vec3 u_color;\n uniform float u_alpha;\n void main() {\n gl_FragColor = vec4(u_color, u_alpha);\n }\n `\n \n const ext = gl.getExtension('OES_element_index_uint')\n if (!ext) {\n console.warn('OES_element_index_uint not supported')\n }\n \n const vs = createShader(gl, gl.VERTEX_SHADER, vsSource)\n const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource)\n program = createProgram(gl, vs, fs)\n \n positionLocation = gl.getAttribLocation(program, 'a_position')\n uColorLocation = gl.getUniformLocation(program, 'u_color')\n uAlphaLocation = gl.getUniformLocation(program, 'u_alpha')\n uMatrixLocation = gl.getUniformLocation(program, 'u_matrix')\n\n // fullscreen quad(用于基于 stencil mask 的上色)\n quadVertexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, quadVertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([\n -1, -1, 0,\n 1, -1, 0,\n 1, 1, 0,\n -1, 1, 0\n ]), gl.STATIC_DRAW)\n\n quadIndexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer)\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW)\n \n // 移除全局 buffer,改用按图层管理的 buffer\n // vertexBuffer = gl.createBuffer()\n // colorBuffer = gl.createBuffer()\n // indexBuffer = gl.createBuffer()\n \n gl.enable(gl.BLEND)\n // 使用加法混合模式 (Additive Blending) 实现颜色叠加 (红+绿=黄)\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE)\n \n resize()\n // 启用 resize 监听,避免窗口变化时画布变形\n window.addEventListener('resize', resize)\n \n canvas.style.cursor = 'grab'\n \n canvas.addEventListener('mousedown', e => {\n if (e.button !== 0) return // 只响应左键\n isDragging = true\n dragStartX = e.clientX\n dragStartY = e.clientY\n dragStartTransX = transX\n dragStartTransY = transY\n canvas.style.cursor = 'grabbing'\n })\n \n window.addEventListener('mousemove', e => {\n if (!isDragging) return\n \n const deltaX = e.clientX - dragStartX\n const deltaY = e.clientY - dragStartY\n const rect = canvas.getBoundingClientRect()\n \n // 将像素偏移转换为归一化设备坐标(NDC)偏移\n const normalizedDeltaX = (deltaX / rect.width) * 2\n const normalizedDeltaY = -(deltaY / rect.height) * 2\n \n transX = dragStartTransX + normalizedDeltaX\n transY = dragStartTransY + normalizedDeltaY\n })\n \n window.addEventListener('mouseup', () => {\n if (isDragging) {\n isDragging = false\n canvas.style.cursor = 'grab'\n }\n })\n \n canvas.addEventListener('mouseleave', () => {\n if (isDragging) {\n isDragging = false\n canvas.style.cursor = 'grab'\n }\n })\n canvas.addEventListener('wheel', e => {\n e.preventDefault()\n \n // 获取鼠标在画布上的位置\n const rect = canvas.getBoundingClientRect()\n const mouseX = e.clientX - rect.left\n const mouseY = e.clientY - rect.top\n \n // 归一化鼠标坐标到 [-1, 1]\n const normalizedX = (mouseX / rect.width) * 2 - 1\n const normalizedY = -((mouseY / rect.height) * 2 - 1)\n \n const canvasAspect = rect.width / rect.height\n \n // 计算鼠标位置在世界坐标系中的坐标(缩放前)\n // 逆向变换:NDC -> 世界坐标\n const worldXBefore = (normalizedX - transX) / (scale / canvasAspect)\n const worldYBefore = (normalizedY - transY) / scale\n \n // 缩放\n const scaleFactor = e.deltaY > 0 ? 0.9 : 1.1\n scale *= scaleFactor\n \n // 计算新的平移,使得鼠标指向的世界坐标点保持不变\n // 世界坐标 -> NDC:x_ndc = x_world * scale / aspect + transX\n // 所以:transX = x_ndc - x_world * scale / aspect\n transX = normalizedX - worldXBefore * (scale / canvasAspect)\n transY = normalizedY - worldYBefore * scale\n })\n}\n\nfunction createShader(gl, type, source) {\n const shader = gl.createShader(type)\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(gl.getShaderInfoLog(shader))\n gl.deleteShader(shader)\n return null\n }\n return shader\n}\n\nfunction createProgram(gl, vs, fs) {\n const p = gl.createProgram()\n gl.attachShader(p, vs)\n gl.attachShader(p, fs)\n gl.linkProgram(p)\n return p\n}\n\nfunction resize() {\n const oldWidth = canvas.width\n const oldHeight = canvas.height\n \n canvas.width = canvas.clientWidth\n canvas.height = canvas.clientHeight\n gl.viewport(0, 0, canvas.width, canvas.height)\n \n // 保持中心点位置不变\n if (oldWidth > 0 && oldHeight > 0 && layers.length > 0) {\n const oldAspect = oldWidth / oldHeight\n const newAspect = canvas.width / canvas.height\n \n // transX 是 NDC 坐标系下的平移量\n // 我们的目标是保持 worldSpace 下的中心点在屏幕上的位置相对不变\n // worldX = (ndcX - transX) / (scale / aspect)\n // ndcX = 0 (屏幕中心) -> centerWorldX = -transX / (scale / oldAspect)\n \n // 我们希望新的 transX' 使得 centerWorldX 对应屏幕中心 (ndc=0)\n // 0 = centerWorldX * (scale / newAspect) + transX'\n // transX' = -centerWorldX * scale / newAspect\n // = -(-transX / (scale / oldAspect)) * scale / newAspect\n // = transX * (scale / oldAspect) * scale / newAspect / scale (错误推导)\n \n // 重新推导:\n // centerWorldX = -transX * oldAspect / scale\n // newTransX = -centerWorldX * scale / newAspect\n // = -(-transX * oldAspect / scale) * scale / newAspect\n // = transX * oldAspect / newAspect\n \n transX = transX * (oldAspect / newAspect)\n }\n\n // 如果已经有图层数据,立即重新渲染\n if (layers.length > 0) {\n cacheNeedsUpdate = true\n requestAnimationFrame(() => {\n if (typeof render === 'function') {\n render()\n }\n })\n }\n}\n\n// ==================== 工具函数 ====================\n\n/**\n * 十六进制颜色转 RGB\n */\nfunction hexToRgb(hex) {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex)\n return result ? {\n r: parseInt(result[1], 16) / 255,\n g: parseInt(result[2], 16) / 255,\n b: parseInt(result[3], 16) / 255\n } : { r: 1, g: 0, b: 0 }\n}\n\n/**\n * 使用 web-gerber 解析 Gerber 和钻孔文件\n */\n// 从同一包里的 Gerber 文件(如 GTL/GBL)提取 %FS/%MO 作为 Excellon(TXT/DRL) 的格式兜底。\n// 目的:当钻孔文件没有显式 FILE_FORMAT/LZ/TZ 时,避免 web-gerber 误判小数点导致孔位“放大10/100倍”或错位。\nlet inheritedGerberFormatHint = null // { integerPlaces, decimalPlaces, places:[int,dec], zero:'L'|'T', unit:'in'|'mm', from:string }\n\nfunction updateInheritedGerberFormatHintFromText(text, fileName) {\n if (!text || typeof text !== 'string') return\n // 只从 Gerber 中提取(排除钻孔:M48)\n if (/\\bM48\\b/i.test(text)) return\n\n const fsMatch = text.match(/%FS([LT])?[AI]X(\\d)(\\d)Y(\\d)(\\d)\\*%/i)\n if (!fsMatch) return\n\n const zero = ((fsMatch[1] || 'L') + '').toUpperCase() === 'T' ? 'T' : 'L'\n const integerPlaces = parseInt(fsMatch[2], 10)\n const decimalPlaces = parseInt(fsMatch[3], 10)\n if (!Number.isFinite(integerPlaces) || !Number.isFinite(decimalPlaces)) return\n\n let unit = null\n const moMatch = text.match(/%MO(IN|MM)\\*%/i)\n if (moMatch) {\n unit = moMatch[1].toUpperCase() === 'IN' ? 'in' : 'mm'\n } else {\n if (/%MOIN\\*%/i.test(text) || /\\bG70\\b/.test(text)) unit = 'in'\n if (/%MOMM\\*%/i.test(text) || /\\bG71\\b/.test(text)) unit = 'mm'\n }\n\n inheritedGerberFormatHint = {\n integerPlaces,\n decimalPlaces,\n totalPlaces: integerPlaces + decimalPlaces,\n places: [integerPlaces, decimalPlaces],\n zero,\n unit,\n from: fileName\n }\n console.log(\n `[格式兜底] 从 ${fileName} 提取 Gerber 格式: ${zero === 'L' ? 'LZ' : 'TZ'} ${integerPlaces}:${decimalPlaces} (unit=${unit || 'unknown'})`\n )\n}\n\nasync function parseWithWebGerber(text, fileName) {\n try {\n console.log(`[web-gerber] 开始解析 ${fileName}`)\n \n // 判断文件类型(钻孔文件含 M48,普通 Gerber 文件不含)\n const isDrillFile = /\\bM48\\b/i.test(text)\n const fileType = isDrillFile ? 'drill' : 'gerber'\n console.log(`[web-gerber] 文件类型: ${fileType}`)\n \n // 对钻孔文件进行格式检测和修正\n let detectedFormat = null\n let drillUnits = null // 'mm' | 'in' | null\n let drillZero = null // 'LZ' | 'TZ' | null\n if (isDrillFile) {\n // 提取格式信息(FILE_FORMAT=X:Y)\n const lines = text.split('\\n').map(line => line.trim())\n \n // 单位/零抑制(Excellon 常见:METRIC,LZ / INCH,TZ / M71 / M72)\n for (const line of lines) {\n if (!drillUnits) {\n if (/^METRIC\\b/i.test(line) || /^M71\\b/i.test(line)) drillUnits = 'mm'\n if (/^INCH\\b/i.test(line) || /^M72\\b/i.test(line)) drillUnits = 'in'\n }\n if (!drillZero) {\n if (/\\bLZ\\b/i.test(line)) drillZero = 'LZ'\n if (/\\bTZ\\b/i.test(line)) drillZero = 'TZ'\n }\n if (drillUnits && drillZero) break\n }\n \n for (const line of lines) {\n const formatMatch = line.match(/FILE_FORMAT[=:](\\d+)[:.](\\d+)/i)\n if (formatMatch) {\n const integerPlaces = parseInt(formatMatch[1])\n const decimalPlaces = parseInt(formatMatch[2])\n detectedFormat = {\n integerPlaces,\n decimalPlaces,\n totalPlaces: integerPlaces + decimalPlaces,\n places: [integerPlaces, decimalPlaces]\n }\n console.log(`[web-gerber] 检测到格式: ${integerPlaces}:${decimalPlaces} (${integerPlaces}位整数, ${decimalPlaces}位小数)`)\n break\n }\n }\n\n // 坐标位数统计(即使没有 FILE_FORMAT 也做),用于推断/兜底\n let maxDigits = 0\n const coordMatchesAll = text.match(/[XY](-?\\d+)/g)\n if (coordMatchesAll) {\n for (const coord of coordMatchesAll) {\n const digits = coord.substring(1).replace(/-/g, '').length\n maxDigits = Math.max(maxDigits, digits)\n }\n }\n\n // 如果 drill 文件没有声明 FILE_FORMAT,则优先继承同包 Gerber 的 FS 格式;否则用坐标位数推断\n if (!detectedFormat) {\n if (inheritedGerberFormatHint && inheritedGerberFormatHint.places) {\n detectedFormat = {\n integerPlaces: inheritedGerberFormatHint.integerPlaces,\n decimalPlaces: inheritedGerberFormatHint.decimalPlaces,\n totalPlaces: inheritedGerberFormatHint.integerPlaces + inheritedGerberFormatHint.decimalPlaces,\n places: [inheritedGerberFormatHint.integerPlaces, inheritedGerberFormatHint.decimalPlaces]\n }\n console.warn(\n `[web-gerber] Drill未声明FILE_FORMAT,继承 Gerber(${inheritedGerberFormatHint.from}) 格式: ${detectedFormat.integerPlaces}:${detectedFormat.decimalPlaces}`\n )\n } else if (maxDigits > 0) {\n // heuristic:优先假设 2 位整数(常见 Excellon/Gerber),其余为小数位\n const integerPlaces = Math.min(2, maxDigits)\n const decimalPlaces = Math.max(0, maxDigits - integerPlaces)\n detectedFormat = {\n integerPlaces,\n decimalPlaces,\n totalPlaces: integerPlaces + decimalPlaces,\n places: [integerPlaces, decimalPlaces]\n }\n console.warn(\n `[web-gerber] Drill未声明FILE_FORMAT,按坐标位数推断格式: ${integerPlaces}:${decimalPlaces} (maxDigits=${maxDigits})`\n )\n }\n }\n\n // 钻孔文件未声明单位/零抑制时,尝试继承 Gerber 的 MO/FS\n if (!drillUnits && inheritedGerberFormatHint && inheritedGerberFormatHint.unit) {\n drillUnits = inheritedGerberFormatHint.unit === 'in' ? 'in' : 'mm'\n }\n if (!drillZero && inheritedGerberFormatHint && inheritedGerberFormatHint.zero) {\n drillZero = inheritedGerberFormatHint.zero === 'T' ? 'TZ' : 'LZ'\n }\n \n // 动态格式检测:检查实际坐标位数\n if (detectedFormat) {\n const coordMatches = coordMatchesAll\n if (coordMatches) {\n // maxDigits 已计算\n \n const definedDigits = detectedFormat.totalPlaces\n console.log(`[web-gerber] 定义格式: [${detectedFormat.integerPlaces}, ${detectedFormat.decimalPlaces}] (${definedDigits}位)`)\n console.log(`[web-gerber] 实际最长坐标: ${maxDigits}位`)\n \n // 如果实际坐标位数超过定义位数,动态增加整数位\n if (maxDigits > definedDigits) {\n const extraDigits = maxDigits - definedDigits\n detectedFormat.integerPlaces += extraDigits\n detectedFormat.totalPlaces = detectedFormat.integerPlaces + detectedFormat.decimalPlaces\n detectedFormat.places = [detectedFormat.integerPlaces, detectedFormat.decimalPlaces]\n console.log(`[web-gerber] ✓ 动态调整格式为: [${detectedFormat.integerPlaces}, ${detectedFormat.decimalPlaces}] (${maxDigits}位)`)\n \n // 修改文件内容中的 FILE_FORMAT 定义\n const oldFormatPattern = /FILE_FORMAT[=:]\\d+[:.]\\d+/i\n const newFormatStr = `FILE_FORMAT=${detectedFormat.integerPlaces}:${detectedFormat.decimalPlaces}`\n \n if (oldFormatPattern.test(text)) {\n text = text.replace(oldFormatPattern, newFormatStr)\n console.log(`[web-gerber] ✓ 已修改文件格式定义 → ${newFormatStr}`)\n }\n }\n }\n }\n }\n \n // 使用 web-gerber 解析(传入修正后的文本和文件类型)\n let parseResult = null\n if (isDrillFile && detectedFormat && detectedFormat.places) {\n // 优先用显式格式解析钻孔(避免小数点位数被库误判)\n const zeroChar = (drillZero || 'LZ').toUpperCase() === 'TZ' ? 'T' : 'L'\n const parseOpts = {\n // 兼容不同版本的 web-gerber:同时提供 type/filetype\n type: 'drill',\n filetype: 'drill',\n format: { places: detectedFormat.places, zero: zeroChar }\n }\n try {\n console.log(\n `[web-gerber] Drill使用显式format解析: places=[${detectedFormat.places[0]},${detectedFormat.places[1]}], zero=${zeroChar}, units=${drillUnits || 'auto'}`\n )\n parseResult = parse(text, parseOpts)\n } catch (e) {\n console.warn(`[web-gerber] Drill显式format解析失败,回退默认解析: ${e?.message || e}`)\n parseResult = null\n }\n }\n if (!parseResult) {\n parseResult = parse(text, { filetype: fileType })\n }\n const plotResult = plot(parseResult)\n \n // console.log(`[web-gerber] plotResult:`, plotResult)\n console.log(`[web-gerber] plotResult.children 数量:`, plotResult.children?.length)\n \n // 使用 renderSVG 将 plotResult 转换成 SVG 树(宏定义会被正确展开)\n const svgTree = renderSVG(plotResult)\n console.log(`[web-gerber] SVG树生成完成, children 数量:`, svgTree?.children?.length)\n\n // 确定是否为轮廓层(GKO、GM1 等)- 检查文件扩展名和文件名\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // 检查扩展名和文件名关键词\n const isOutlineLayer = \n fileExtension === '.GKO' || \n fileExtension === '.GM' || \n /^\\.GM\\d+$/i.test(fileExtension) ||\n fileNameUpper.includes('OUTLINE') ||\n /EDGE[._-]CUTS/i.test(fileNameUpper) ||\n fileNameUpper.includes('WX') ||\n fileNameUpper.includes('BOARD') ||\n fileNameUpper.startsWith('GKO') ||\n fileNameUpper.startsWith('OUT')\n \n // 轮廓层最小线宽 0.5mm,确保可见\n const minWidth = isOutlineLayer ? 0.3 : 0\n \n // 轮廓层打印识别日志\n if (isOutlineLayer) {\n console.log(`[轮廓层] ${fileNameOnly}: minWidth=${minWidth}mm`)\n }\n \n const vertices = []\n const indices = []\n // drillPosScaleRuns:仅用于“钻孔坐标位数/零抑制误解读”导致的整体缩放错位时,\n // 后续按参考层 bounds 进行“只缩放孔位、不缩放孔径”的修正。\n // 记录每个 circle/rectangle 等由“中心+尺寸”生成的顶点区间,便于只移动中心而不缩放半径/孔径。\n const drillPosScaleRuns = isDrillFile ? [] : null\n // polarityRuns:记录 indices 的区间 [start,end),用于渲染阶段按 LPC/LPD 做 stencil 挖空\n // 初始默认为 dark(Gerber 默认极性)\n const polarityRuns = []\n let currentPolarity = 'dark'\n let currentRunStart = 0\n const ensurePolarity = (nextPolarity) => {\n const p = (nextPolarity || 'dark')\n if (p === currentPolarity) return\n if (indices.length > currentRunStart) {\n polarityRuns.push({ polarity: currentPolarity, start: currentRunStart, end: indices.length })\n }\n currentPolarity = p\n currentRunStart = indices.length\n }\n const scale = plotResult.units === 'in' ? 25.4 : 1 // 英寸转毫米\n \n // ==================== Drill: FILE_FORMAT 坐标缩放修正(只修正位置,不放大孔径)====================\n // 部分 Excellon 会用注释 `;FILE_FORMAT=4:4` 指定隐式小数点位数,但解析器可能按默认格式解释坐标,\n // 导致所有孔位 XY 缩小 10/100 倍,看起来“只渲染出一个点”。\n // 这里:用首个 X/Y 数字串按 detectedFormat 推导期望坐标,再与 plotResult.children 的首个 circle.cx/cy 对比,\n // 得到一个 posScaleFix(用于坐标位置)。\n let coordPosScaleFix = 1\n const parseFixedCoord = (rawDigits, fmt, zeroMode) => {\n if (!fmt || rawDigits == null) return null\n const s = String(rawDigits).trim()\n const sign = s.startsWith('-') ? -1 : 1\n let digits = s.replace(/[^0-9]/g, '')\n if (!digits) return null\n const need = fmt.totalPlaces\n if (need && digits.length < need) {\n // LZ (Leading Zero suppression) = 前导零被省略 → 需要左侧补零\n // TZ (Trailing Zero suppression) = 尾部零被省略 → 需要右侧补零\n if ((zeroMode || '').toUpperCase() === 'LZ') {\n digits = digits.padStart(need, '0') // LZ: 左侧补零\n } else {\n digits = digits.padEnd(need, '0') // TZ: 右侧补零\n }\n }\n const denom = Math.pow(10, fmt.decimalPlaces || 0)\n const n = parseInt(digits, 10)\n if (!Number.isFinite(n) || denom === 0) return null\n return sign * (n / denom)\n }\n\n if (isDrillFile && detectedFormat && Array.isArray(plotResult.children) && plotResult.children.length > 0) {\n try {\n const xyMatch = text.match(/X(-?\\d+)\\s*Y(-?\\d+)/i)\n const firstCircle = plotResult.children.find(c =>\n c?.type === 'imageShape' &&\n c.shape?.type === 'circle' &&\n Number.isFinite(c.shape.cx) &&\n Number.isFinite(c.shape.cy)\n )\n\n if (xyMatch && firstCircle) {\n const actX = firstCircle.shape.cx\n const actY = firstCircle.shape.cy\n\n // 将期望坐标转换到 plotResult.units(in/mm)\n const plotUnits = plotResult.units === 'in' ? 'in' : 'mm'\n const srcUnits = drillUnits || plotUnits\n\n const evalZeroMode = (zeroMode) => {\n let expX = parseFixedCoord(xyMatch[1], detectedFormat, zeroMode)\n let expY = parseFixedCoord(xyMatch[2], detectedFormat, zeroMode)\n if (expX != null && expY != null) {\n if (srcUnits === 'in' && plotUnits === 'mm') { expX *= 25.4; expY *= 25.4 }\n if (srcUnits === 'mm' && plotUnits === 'in') { expX /= 25.4; expY /= 25.4 }\n }\n const ratios = []\n if (Number.isFinite(expX) && actX !== 0) ratios.push(expX / actX)\n if (Number.isFinite(expY) && actY !== 0) ratios.push(expY / actY)\n if (ratios.length === 0) return null\n const avg = ratios.reduce((s, v) => s + v, 0) / ratios.length\n const max = Math.max(...ratios)\n const min = Math.min(...ratios)\n const spread = (avg !== 0) ? Math.abs(max - min) / Math.abs(avg) : 999\n return { zeroMode, expX, expY, avg, spread }\n }\n\n // 某些 Excellon 头会写 INCH,LZ 但实际更像 TZ(或反之),\n // 如果我们只信 header 去推导 expected,会误触发 posScaleFix 把孔缩到角落。\n const primaryZero = (drillZero || '').toUpperCase() === 'TZ' ? 'TZ' : 'LZ'\n const altZero = primaryZero === 'TZ' ? 'LZ' : 'TZ'\n const primary = evalZeroMode(primaryZero)\n const alt = evalZeroMode(altZero)\n\n const near1 = (r) => r && Number.isFinite(r.avg) && r.spread < 0.05 && Math.abs(r.avg - 1) <= 0.05\n\n if (near1(primary)) {\n // 已匹配,无需缩放\n } else if (near1(alt)) {\n console.warn(`[web-gerber] ⚠️ Drill零抑制声明为 ${primaryZero},但坐标更符合 ${altZero}(ratio≈1),跳过 posScaleFix 以避免误缩放`)\n } else {\n // 两种零抑制都不接近 1,才认为可能存在“整体倍率错误”,按 header 优先推断\n const chosen = primary || alt\n if (chosen && Number.isFinite(chosen.avg) && Math.abs(chosen.avg - 1) > 0.05 && chosen.spread < 0.05) {\n const absAvg = Math.abs(chosen.avg)\n const pow10 = Math.pow(10, Math.round(Math.log10(absAvg)))\n const snapped = (Math.abs(absAvg - pow10) / pow10) < 0.15 ? pow10 : absAvg\n if (snapped >= 0.01 && snapped <= 10000) {\n coordPosScaleFix = snapped\n console.warn(`[web-gerber] ⚠️ Drill坐标格式疑似被错误解释,启用 FILE_FORMAT 坐标缩放修正:posScaleFix=${coordPosScaleFix} (units=${plotUnits}, zero=${chosen.zeroMode})`)\n }\n }\n }\n }\n } catch (e) {\n console.warn('[web-gerber] Drill FILE_FORMAT 坐标缩放修正失败,忽略:', e?.message || e)\n }\n }\n\n const posScale = scale * coordPosScaleFix // 仅用于坐标“位置”\n const sizeScale = scale // 用于孔径/线宽等“尺寸”(不要乘 posScaleFix)\n \n /**\n * 从 SVG 树提取元素并转换为实心三角形\n */\n const processSvgElement = (element) => {\n if (!element || !element.tagName) return\n \n const tagName = element.tagName.toLowerCase()\n \n // 处理圆形 → 三角扇形(实心)\n if (tagName === 'circle') {\n const cx = parseFloat(element.properties?.cx || element.attributes?.cx || 0) * scale\n const cy = parseFloat(element.properties?.cy || element.attributes?.cy || 0) * scale * (-1) // 翻转Y轴\n const r = parseFloat(element.properties?.r || element.attributes?.r || 0) * scale\n \n if (r > 0) {\n const segments = Math.min(Math.max(Math.ceil(2 * Math.PI * r / 0.2), 12), 64)\n const baseIndex = vertices.length / 3\n \n // 中心点\n vertices.push(cx, cy, 0)\n \n // 圆周点\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2\n vertices.push(cx + Math.cos(angle) * r, cy + Math.sin(angle) * r, 0)\n }\n \n // 三角形索引(扇形)\n for (let i = 0; i < segments; i++) {\n indices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 额外添加10个中心点(用于特征提取,确保顶点簇检测能识别)\n for (let i = 0; i < 10; i++) {\n vertices.push(cx, cy, 0)\n }\n }\n }\n // 处理椭圆 → 三角扇形(实心)\n else if (tagName === 'ellipse') {\n const cx = parseFloat(element.properties?.cx || element.attributes?.cx || 0) * scale\n const cy = parseFloat(element.properties?.cy || element.attributes?.cy || 0) * scale * (-1) // 翻转Y轴\n const rx = parseFloat(element.properties?.rx || element.attributes?.rx || 0) * scale\n const ry = parseFloat(element.properties?.ry || element.attributes?.ry || 0) * scale\n \n if (rx > 0 && ry > 0) {\n const segments = Math.min(Math.max(Math.ceil(2 * Math.PI * Math.max(rx, ry) / 0.2), 12), 64)\n const baseIndex = vertices.length / 3\n \n // 中心点\n vertices.push(cx, cy, 0)\n \n // 椭圆周点\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2\n vertices.push(cx + Math.cos(angle) * rx, cy + Math.sin(angle) * ry, 0)\n }\n \n // 三角形索引(扇形)\n for (let i = 0; i < segments; i++) {\n indices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 额外添加10个中心点(用于特征提取)\n for (let i = 0; i < 10; i++) {\n vertices.push(cx, cy, 0)\n }\n }\n }\n // 处理矩形 → 2个三角形(实心)或圆角矩形\n else if (tagName === 'rect') {\n const x = parseFloat(element.properties?.x || element.attributes?.x || 0) * scale\n const y = parseFloat(element.properties?.y || element.attributes?.y || 0) * scale * (-1) // 翻转Y轴\n const width = parseFloat(element.properties?.width || element.attributes?.width || 0) * scale\n const height = parseFloat(element.properties?.height || element.attributes?.height || 0) * scale\n const rx = parseFloat(element.properties?.rx || element.attributes?.rx || 0) * scale\n const ry = parseFloat(element.properties?.ry || element.attributes?.ry || 0) * scale\n \n if (width > 0 && height > 0) {\n // 检查是否有圆角(Obround)\n if (rx > 0 || ry > 0) {\n // 圆角矩形 - 渲染成药丸形状(中间矩形 + 两端半圆)\n const cornerRadius = Math.max(rx, ry)\n \n // 判断方向:横向还是纵向\n if (width > height) {\n // 横向长圆形(两端半圆在左右)\n const radius = height / 2\n const rectWidth = width - height // 中间矩形的宽度\n \n // 左半圆\n const leftCx = x + radius\n const leftCy = y - radius\n const segments = Math.max(12, Math.ceil(radius * 2))\n let centerIdx = vertices.length / 3\n vertices.push(leftCx, leftCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(leftCx + Math.cos(angle) * radius, leftCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n \n // 中间矩形\n if (rectWidth > 0) {\n const baseIndex = vertices.length / 3\n vertices.push(x + radius, y, 0)\n vertices.push(x + radius + rectWidth, y, 0)\n vertices.push(x + radius + rectWidth, y - height, 0)\n vertices.push(x + radius, y - height, 0)\n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n }\n \n // 右半圆\n const rightCx = x + width - radius\n const rightCy = y - radius\n centerIdx = vertices.length / 3\n vertices.push(rightCx, rightCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(rightCx + Math.cos(angle) * radius, rightCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n } else {\n // 纵向长圆形(两端半圆在上下)\n const radius = width / 2\n const rectHeight = height - width // 中间矩形的高度\n \n // 上半圆\n const topCx = x + radius\n const topCy = y - radius\n const segments = Math.max(12, Math.ceil(radius * 2))\n let centerIdx = vertices.length / 3\n vertices.push(topCx, topCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(topCx + Math.cos(angle) * radius, topCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n \n // 中间矩形\n if (rectHeight > 0) {\n const baseIndex = vertices.length / 3\n vertices.push(x, y - radius, 0)\n vertices.push(x + width, y - radius, 0)\n vertices.push(x + width, y - radius - rectHeight, 0)\n vertices.push(x, y - radius - rectHeight, 0)\n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n }\n \n // 下半圆\n const bottomCx = x + radius\n const bottomCy = y - height + radius\n centerIdx = vertices.length / 3\n vertices.push(bottomCx, bottomCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(bottomCx + Math.cos(angle) * radius, bottomCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n } else {\n // 普通矩形(无圆角)\n const baseIndex = vertices.length / 3\n // 注意:Y轴翻转后,需要调整矩形的绘制顺序\n vertices.push(x, y, 0)\n vertices.push(x + width, y, 0)\n vertices.push(x + width, y - height, 0) // y - height 因为Y轴已翻转\n vertices.push(x, y - height, 0)\n \n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n }\n }\n }\n // 处理路径(宏定义展开的复杂形状)\n else if (tagName === 'path') {\n const d = element.properties?.d || element.attributes?.d || ''\n const fill = element.properties?.fill || element.attributes?.fill || ''\n const stroke = element.properties?.stroke || element.attributes?.stroke || ''\n \n // 判断是填充路径还是线条路径\n const isFillPath = fill && fill !== 'none'\n const isStrokePath = stroke && stroke !== 'none'\n \n if (d) {\n const pathPoints = parseSvgPath(d, scale)\n \n // 如果是填充路径 → earcut 三角化(实心)\n if (isFillPath && pathPoints && pathPoints.length >= 6) {\n try {\n const triangles = earcut(pathPoints)\n if (triangles && triangles.length > 0) {\n const baseIndex = vertices.length / 3\n for (let i = 0; i < pathPoints.length; i += 2) {\n vertices.push(pathPoints[i], pathPoints[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n } catch (e) {\n console.warn('[SVG Path Fill] 三角化失败:', e)\n }\n }\n // 如果是线条路径 → 生成带宽度的线段 + 圆形端点\n else if (isStrokePath && pathPoints && pathPoints.length >= 4) {\n let strokeWidth = parseFloat(element.properties?.['stroke-width'] || element.attributes?.['stroke-width'] || 0.15) * scale\n if (strokeWidth < minWidth) strokeWidth = minWidth\n \n const radius = strokeWidth / 2\n \n // 将路径转换为一系列线段 + 端点\n for (let i = 0; i < pathPoints.length - 2; i += 2) {\n const x1 = pathPoints[i]\n const y1 = pathPoints[i + 1]\n const x2 = pathPoints[i + 2]\n const y2 = pathPoints[i + 3]\n \n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n \n if (len > 0.001) { // 忽略极短的线段\n const nx = -dy / len * radius\n const ny = dx / len * radius\n \n // 绘制线段主体(矩形)\n const baseIndex = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex + 1, baseIndex + 3, baseIndex + 2)\n \n // 在起点添加圆形端点(只在第一个线段添加)\n if (i === 0) {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(x1, y1, 0) // 中心点\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x1 + Math.cos(angle) * radius, y1 + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 在终点添加圆形端点(每个线段的终点)\n {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(x2, y2, 0) // 中心点\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x2 + Math.cos(angle) * radius, y2 + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n }\n }\n }\n }\n // 处理线条 → 带宽度的矩形 + 圆形端点(实心)\n else if (tagName === 'line') {\n const x1 = parseFloat(element.properties?.x1 || element.attributes?.x1 || 0) * scale\n const y1 = parseFloat(element.properties?.y1 || element.attributes?.y1 || 0) * scale * (-1) // 翻转Y轴\n const x2 = parseFloat(element.properties?.x2 || element.attributes?.x2 || 0) * scale\n const y2 = parseFloat(element.properties?.y2 || element.attributes?.y2 || 0) * scale * (-1) // 翻转Y轴\n \n // 从 SVG 属性中获取线宽\n let strokeWidth = parseFloat(element.properties?.['stroke-width'] || element.attributes?.['stroke-width'] || 0.15) * scale\n \n // 应用最小线宽\n if (strokeWidth < minWidth) strokeWidth = minWidth\n \n const radius = strokeWidth / 2\n \n // 绘制矩形线段\n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len > 0) {\n const nx = -dy / len * radius\n const ny = dx / len * radius\n \n // 线段主体\n const baseIndex = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex + 1, baseIndex + 3, baseIndex + 2)\n \n // 起点圆形端点\n const segments = Math.max(8, Math.ceil(radius * 2))\n let centerIdx = vertices.length / 3\n vertices.push(x1, y1, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x1 + Math.cos(angle) * radius, y1 + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n \n // 终点圆形端点\n centerIdx = vertices.length / 3\n vertices.push(x2, y2, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x2 + Math.cos(angle) * radius, y2 + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n // 处理多段线 polyline\n else if (tagName === 'polyline' || tagName === 'polygon') {\n const pointsStr = element.properties?.points || element.attributes?.points || ''\n if (pointsStr) {\n const coords = pointsStr.trim().split(/[\\s,]+/).map(parseFloat)\n if (coords.length >= 6) { // 至少3个点\n const pathPoints = []\n for (let i = 0; i < coords.length; i += 2) {\n if (!isNaN(coords[i]) && !isNaN(coords[i + 1])) {\n pathPoints.push(coords[i] * scale, coords[i + 1] * scale * (-1)) // 翻转Y轴\n }\n }\n \n if (pathPoints.length >= 6) {\n try {\n const triangles = earcut(pathPoints)\n if (triangles && triangles.length > 0) {\n const baseIndex = vertices.length / 3\n for (let i = 0; i < pathPoints.length; i += 2) {\n vertices.push(pathPoints[i], pathPoints[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n } catch (e) {\n console.warn('[SVG Polyline] 三角化失败:', e)\n }\n }\n }\n }\n }\n // 递归处理子元素(group 等容器)\n if (element.children && element.children.length > 0) {\n element.children.forEach(child => processSvgElement(child))\n }\n }\n \n /**\n * 简化的 SVG 路径解析器\n */\n function parseSvgPath(d, scale) {\n const coords = []\n const commands = d.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi) || []\n let currentX = 0, currentY = 0\n let startX = 0, startY = 0\n \n commands.forEach(cmd => {\n const type = cmd[0].toUpperCase()\n const isRelative = cmd[0] === cmd[0].toLowerCase()\n const args = cmd.slice(1).trim().split(/[\\s,]+/).filter(s => s).map(parseFloat)\n \n switch (type) {\n case 'M': // Move\n if (args.length >= 2) {\n currentX = isRelative ? currentX + args[0] : args[0]\n currentY = isRelative ? currentY + args[1] : args[1]\n startX = currentX\n startY = currentY\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'L': // Line\n if (args.length >= 2) {\n currentX = isRelative ? currentX + args[0] : args[0]\n currentY = isRelative ? currentY + args[1] : args[1]\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'H': // Horizontal line\n if (args.length >= 1) {\n currentX = isRelative ? currentX + args[0] : args[0]\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'V': // Vertical line\n if (args.length >= 1) {\n currentY = isRelative ? currentY + args[0] : args[0]\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'Z': // Close path\n if (currentX !== startX || currentY !== startY) {\n coords.push(startX * scale, startY * scale * (-1)) // 翻转Y轴\n }\n break\n }\n })\n \n return coords\n }\n \n // 处理 SVG 树\n // 钻孔文件:plotResult.children 已包含每个孔的 imageShape.circle,且孔径来自显式小数,可靠;\n // 同时处理 svgTree 容易重复生成一套孔位,增加三角形数且可能引入坐标差异。\n if (!isDrillFile && svgTree && svgTree.children) {\n svgTree.children.forEach(child => processSvgElement(child))\n console.log(`[web-gerber] 从SVG树提取: ${vertices.length / 3} 顶点, ${indices.length / 3} 三角形`)\n }\n \n // 同时处理 plotResult.children(包含填充区域等SVG树可能遗漏的数据)\n if (plotResult.children && plotResult.children.length > 0) {\n const verticesBefore = vertices.length / 3\n console.log(`[web-gerber] 补充处理 plotResult.children (${plotResult.children.length} 个)`)\n \n // 直接从 plotResult.children 中提取几何数据\n // 统计各种类型的数量\n const typeCount = {}\n plotResult.children.forEach(child => {\n typeCount[child.type] = (typeCount[child.type] || 0) + 1\n })\n // console.log(`[web-gerber] 类型统计:`, typeCount)\n \n // 注意:这里不要重排 children 的顺序。\n // 因为 Gerber 的 LPC/LPD(clear/dark)是“有状态”的顺序布尔操作,\n // 若重排会导致清除/回填顺序被打乱,最终形状会缺失/错误。\n plotResult.children.forEach((child, idx) => {\n // 先根据 child.polarity 切换 polarity run(只影响后续写入的 indices 区间)\n ensurePolarity(child.polarity || 'dark')\n\n // scale 已经在外层定义了,这里直接使用\n \n // 处理 imagePath(线条和圆弧路径 - 普通Gerber文件)\n if (child.type === 'imagePath' && child.segments && child.segments.length > 0) {\n \n // 将路径离散化为线段 + 圆形端点\n child.segments.forEach((segment, segIdx) => {\n if (segment.type === 'line' && segment.start && segment.end) {\n const startX = segment.start[0] * scale\n const startY = segment.start[1] * scale\n const endX = segment.end[0] * scale\n const endY = segment.end[1] * scale\n \n const baseIndex = vertices.length / 3\n vertices.push(startX, startY, 0)\n vertices.push(endX, endY, 0)\n \n // 用三角形模拟线条:按 segment/child 真实线宽,缺省 0.15mm\n let width = ((segment.width ?? child.width ?? 0.15) * scale)\n \n // 应用最小线宽 (GKO层)\n if (width < minWidth) width = minWidth\n \n const radius = width / 2\n\n const dx = endX - startX\n const dy = endY - startY\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len > 0) {\n const nx = -dy / len * radius\n const ny = dx / len * radius\n \n // 线段主体(矩形)\n const v1 = vertices.length / 3\n vertices.push(startX + nx, startY + ny, 0)\n vertices.push(startX - nx, startY - ny, 0)\n vertices.push(endX + nx, endY + ny, 0)\n vertices.push(endX - nx, endY - ny, 0)\n \n indices.push(v1, v1 + 1, v1 + 2)\n indices.push(v1 + 1, v1 + 3, v1 + 2)\n \n // 在起点添加圆形端点(只在第一个线段添加)\n if (segIdx === 0) {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(startX, startY, 0)\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(startX + Math.cos(angle) * radius, startY + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 在终点添加圆形端点(每个线段的终点)\n {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(endX, endY, 0)\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(endX + Math.cos(angle) * radius, endY + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n } else if (segment.type === 'arc') {\n // 圆弧离散化(优先使用 web-gerber 提供的 theta,避免 0/2π 信息丢失)\n const sx = segment.start[0]\n const sy = segment.start[1]\n const ex = segment.end[0]\n const ey = segment.end[1]\n const cx0 = segment.center[0]\n const cy0 = segment.center[1]\n\n const startX = sx * scale\n const startY = sy * scale\n const endX = ex * scale\n const endY = ey * scale\n const cx = cx0 * scale\n const cy = cy0 * scale\n \n const startAngle = (segment.start.length > 2 && typeof segment.start[2] === 'number')\n ? segment.start[2]\n : Math.atan2(sy - cy0, sx - cx0)\n const endAngle = (segment.end.length > 2 && typeof segment.end[2] === 'number')\n ? segment.end[2]\n : Math.atan2(ey - cy0, ex - cx0)\n \n const arcRadius = ((typeof segment.radius === 'number' && segment.radius > 0)\n ? segment.radius\n : Math.sqrt(Math.pow(sx - cx0, 2) + Math.pow(sy - cy0, 2))) * scale\n \n // 检测是否为完整的圆(起点和终点相同)\n const isFullCircle = Math.abs(startX - endX) < 0.001 && Math.abs(startY - endY) < 0.001\n let angleDiff = endAngle - startAngle\n \n if (isFullCircle) {\n // 完整的圆,画360度\n angleDiff = Math.PI * 2\n if (idx < 5) {\n // console.log(`[web-gerber] 检测到完整圆弧: 圆心(${cx.toFixed(2)}, ${cy.toFixed(2)}), 半径=${arcRadius.toFixed(2)}mm`)\n }\n } else if (Math.abs(angleDiff) < 0.001) {\n // 角度差太小,跳过\n return\n }\n \n const arcSegments = Math.max(8, Math.ceil(Math.abs(angleDiff) * arcRadius / 0.5))\n // 圆弧线宽:按 segment/child 真实线宽,缺省 0.15mm\n let arcWidth = ((segment.width ?? child.width ?? 0.15) * scale)\n \n // 应用最小线宽 (GKO层)\n if (arcWidth < minWidth) arcWidth = minWidth\n \n const lineRadius = arcWidth / 2 // 线宽的半径\n\n for (let i = 0; i < arcSegments; i++) {\n const t1 = i / arcSegments\n const t2 = (i + 1) / arcSegments\n const angle1 = startAngle + angleDiff * t1\n const angle2 = startAngle + angleDiff * t2\n \n const x1 = cx + Math.cos(angle1) * arcRadius\n const y1 = cy + Math.sin(angle1) * arcRadius\n const x2 = cx + Math.cos(angle2) * arcRadius\n const y2 = cy + Math.sin(angle2) * arcRadius\n \n // 为圆弧添加线宽(用三角形模拟)\n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len > 0) {\n const nx = -dy / len * lineRadius\n const ny = dx / len * lineRadius\n \n const v1 = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(v1, v1 + 1, v1 + 2)\n indices.push(v1 + 1, v1 + 3, v1 + 2)\n }\n }\n \n // 在圆弧起点添加圆形端点(只在第一个segment或非完整圆时添加)\n if (!isFullCircle && segIdx === 0) {\n const capSegments = Math.max(8, Math.ceil(lineRadius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(startX, startY, 0)\n \n for (let j = 0; j <= capSegments; j++) {\n const angle = (j / capSegments) * Math.PI * 2\n vertices.push(startX + Math.cos(angle) * lineRadius, startY + Math.sin(angle) * lineRadius, 0)\n }\n \n for (let j = 0; j < capSegments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 在圆弧终点添加圆形端点(非完整圆时添加)\n if (!isFullCircle) {\n const capSegments = Math.max(8, Math.ceil(lineRadius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(endX, endY, 0)\n \n for (let j = 0; j <= capSegments; j++) {\n const angle = (j / capSegments) * Math.PI * 2\n vertices.push(endX + Math.cos(angle) * lineRadius, endY + Math.sin(angle) * lineRadius, 0)\n }\n \n for (let j = 0; j < capSegments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n })\n }\n // 处理 imageShape(圆形和其他形状)\n else if (child.type === 'imageShape' && child.shape) {\n const shape = child.shape\n const polarity = child.polarity || 'dark' // 默认为 dark\n \n // if (idx < 10) {\n // console.log(`[web-gerber] Shape ${idx}:`, shape, `polarity: ${polarity}`)\n // }\n \n // web-gerber 的 shape.type 主要是:\n // - circle / rectangle / polygon / outline / layeredShape\n // 之前这里只处理了 circle + (误写的 rect),会导致部分 pad/宏形状缺失。\n\n const emitCircle = (cx, cy, r) => {\n if (!(r > 0)) return\n // 位置坐标应用 posScale(包含 FILE_FORMAT 坐标缩放修正);孔径来自显式小数,不应被放大\n const x = cx * posScale\n const y = cy * posScale\n const radius = r * sizeScale\n \n const segments = Math.min(Math.max(Math.ceil(2 * Math.PI * radius / 0.2), 12), 64)\n const baseIndex = vertices.length / 3\n \n // 中心点\n vertices.push(x, y, 0)\n // 圆周点\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2\n vertices.push(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius, 0)\n }\n for (let i = 0; i < segments; i++) {\n indices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 额外添加10个中心点(用于特征提取)\n for (let i = 0; i < 10; i++) {\n vertices.push(x, y, 0)\n }\n\n // 记录本次 circle 的顶点区间(用于后续只缩放孔位)\n if (drillPosScaleRuns) {\n const endIndex = vertices.length / 3\n drillPosScaleRuns.push({ start: baseIndex, count: endIndex - baseIndex, cx: x, cy: y, kind: 'circle' })\n }\n }\n\n const emitRectangle = (x, y, xSize, ySize, r) => {\n const x0 = (x || 0) * posScale\n const y0 = (y || 0) * posScale\n const w = (xSize || 0) * sizeScale\n const h = (ySize || 0) * sizeScale\n if (!(w > 0 && h > 0)) return\n\n // r 是可选圆角半径(单位同 plot units),这里先按直角矩形渲染,避免“缺失”\n const baseIndex = vertices.length / 3\n vertices.push(x0, y0, 0)\n vertices.push(x0 + w, y0, 0)\n vertices.push(x0 + w, y0 + h, 0)\n vertices.push(x0, y0 + h, 0)\n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n\n // 额外添加中心点(用于特征提取)\n // 很多通孔焊盘/阻焊开窗是“长圆形/圆角矩形”(obround),如果只提取 circle 圆心会导致参考层特征不足,\n // 从而在钻孔只有“一条直线分布”时匹配不稳定。\n const cx = x0 + w / 2\n const cy = y0 + h / 2\n for (let i = 0; i < 10; i++) {\n vertices.push(cx, cy, 0)\n }\n\n // 记录本次 rectangle 的顶点区间(用于后续只缩放孔位)\n if (drillPosScaleRuns) {\n const endIndex = vertices.length / 3\n drillPosScaleRuns.push({ start: baseIndex, count: endIndex - baseIndex, cx, cy, kind: 'rectangle' })\n }\n }\n\n const emitPolygon = (pts) => {\n if (!Array.isArray(pts) || pts.length < 3) return\n const flat = []\n for (const p of pts) {\n if (!p || p.length < 2) continue\n flat.push(p[0] * posScale, p[1] * posScale)\n }\n if (flat.length < 6) return\n\n const triangles = earcut(flat)\n if (!triangles || triangles.length === 0) return\n const baseIndex = vertices.length / 3\n for (let i = 0; i < flat.length; i += 2) {\n vertices.push(flat[i], flat[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n\n const emitOutline = (segments) => {\n if (!Array.isArray(segments) || segments.length === 0) return\n const pts = []\n let started = false\n\n const pushPoint = (x, y) => {\n pts.push(x * posScale, y * posScale)\n }\n\n for (const seg of segments) {\n if (!seg || !seg.type) continue\n\n if (seg.type === 'line' && seg.start && seg.end) {\n if (!started) {\n pushPoint(seg.start[0], seg.start[1])\n started = true\n }\n pushPoint(seg.end[0], seg.end[1])\n } else if (seg.type === 'arc' && seg.start && seg.end && seg.center) {\n // ArcPosition: [x,y,theta],theta 为弧度\n const cx = seg.center[0] * posScale\n const cy = seg.center[1] * posScale\n const radius = (seg.radius ?? Math.sqrt(Math.pow(seg.start[0] - seg.center[0], 2) + Math.pow(seg.start[1] - seg.center[1], 2))) * posScale\n\n const startTheta = (seg.start.length > 2 ? seg.start[2] : Math.atan2(seg.start[1] - seg.center[1], seg.start[0] - seg.center[0]))\n let endTheta = (seg.end.length > 2 ? seg.end[2] : Math.atan2(seg.end[1] - seg.center[1], seg.end[0] - seg.center[0]))\n // 假定 arc 按 CCW,从 startTheta 走到 endTheta(跨 0 则 +2π)\n if (endTheta < startTheta) endTheta += Math.PI * 2\n\n if (!started) {\n pushPoint(seg.start[0], seg.start[1])\n started = true\n }\n\n const arcLen = Math.abs(endTheta - startTheta) * radius\n const arcSegments = Math.max(8, Math.ceil(arcLen / 0.5))\n for (let i = 1; i <= arcSegments; i++) {\n const t = i / arcSegments\n const theta = startTheta + (endTheta - startTheta) * t\n const x = cx + Math.cos(theta) * radius\n const y = cy + Math.sin(theta) * radius\n pts.push(x, y)\n }\n }\n }\n\n if (pts.length < 6) return\n const triangles = earcut(pts)\n if (!triangles || triangles.length === 0) return\n const baseIndex = vertices.length / 3\n for (let i = 0; i < pts.length; i += 2) {\n vertices.push(pts[i], pts[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n\n const emitSimpleShape = (s) => {\n if (!s || !s.type) return\n if (s.type === 'circle') {\n emitCircle(s.cx || 0, s.cy || 0, s.r || 0)\n } else if (s.type === 'rectangle') {\n emitRectangle(s.x || 0, s.y || 0, s.xSize || 0, s.ySize || 0, s.r || 0)\n } else if (s.type === 'polygon') {\n emitPolygon(s.points || [])\n } else if (s.type === 'outline') {\n emitOutline(s.segments || [])\n }\n }\n\n // layeredShape(宏)可能包含 erase 子形状(需要在同一图层内“挖空”)\n if (shape.type === 'layeredShape' && Array.isArray(shape.shapes)) {\n for (const sub of shape.shapes) {\n const localPolarity = sub && sub.erase === true ? 'clear' : polarity\n ensurePolarity(localPolarity)\n emitSimpleShape(sub)\n }\n ensurePolarity(polarity)\n } else {\n // 普通 shape:沿用 child 的 polarity\n emitSimpleShape(shape)\n }\n }\n // 处理 imageRegion(区域填充 - G36/G37)\n else if (child.type === 'imageRegion' && child.segments && child.segments.length > 0) {\n // 打印所有 Region 的基本信息(已禁用以减少日志量)\n // console.log(`[web-gerber] Region ${idx}: segments=${child.segments.length}, 类型=${child.segments.map(s => s.type).join(',')}`)\n \n // ⚠️ 不在此处过滤任何内容,确保 Gerber图/骨架线框模式显示完整\n // 字符孔洞的处理交给仿真模式专用的 triangulateSilkscreenFromSvg 流程\n \n // 提取区域轮廓点(用于三角化填充)\n const regionPoints = []\n const holeIndices = []\n let lastEnd = null\n \n child.segments.forEach((segment, segIdx) => {\n let startX, startY, endX, endY\n let isArc = false\n let arcParams = null\n \n if (segment.type === 'line') {\n startX = segment.start[0] * scale\n startY = segment.start[1] * scale\n endX = segment.end[0] * scale\n endY = segment.end[1] * scale\n } else if (segment.type === 'arc') {\n isArc = true\n // web-gerber 的 arc.start/end 是 ArcPosition: [x, y, theta],theta 很关键:\n // 例如点在右侧时,theta 可能是 0 或 2π,用 atan2 计算会丢失 2π 的信息,\n // 从而把“下半圆”错误地走成“上半圆”(SlotHoles 会缺一个圆帽)。\n const sx = segment.start[0]\n const sy = segment.start[1]\n const ex = segment.end[0]\n const ey = segment.end[1]\n\n startX = sx * scale\n startY = sy * scale\n endX = ex * scale\n endY = ey * scale\n\n const cx0 = segment.center[0]\n const cy0 = segment.center[1]\n const cx = cx0 * scale\n const cy = cy0 * scale\n\n const startAngle = (segment.start.length > 2 && typeof segment.start[2] === 'number')\n ? segment.start[2]\n : Math.atan2(sy - cy0, sx - cx0)\n const endAngle = (segment.end.length > 2 && typeof segment.end[2] === 'number')\n ? segment.end[2]\n : Math.atan2(ey - cy0, ex - cx0)\n\n const radius0 = (typeof segment.radius === 'number' && segment.radius > 0)\n ? segment.radius\n : Math.sqrt(Math.pow(sx - cx0, 2) + Math.pow(sy - cy0, 2))\n const radius = radius0 * scale\n\n // 按 web-gerber 提供的 theta 方向走(不做“最短弧”归一化)\n let angleDiff = endAngle - startAngle\n\n // 检测是否为完整的圆(起点和终点相同且角度差接近0)\n const isSamePoint = Math.abs(sx - ex) < 1e-9 && Math.abs(sy - ey) < 1e-9\n if (isSamePoint && Math.abs(angleDiff) < 1e-6) {\n angleDiff = Math.PI * 2\n }\n \n arcParams = { cx, cy, radius, startAngle, angleDiff }\n }\n \n // 检查断点(新轮廓)\n let isNewContour = false\n if (lastEnd) {\n const dist = Math.sqrt(Math.pow(startX - lastEnd.x, 2) + Math.pow(startY - lastEnd.y, 2))\n if (dist > 0.01) { // 0.01mm 容差\n isNewContour = true\n holeIndices.push(regionPoints.length / 2)\n }\n } else {\n isNewContour = true // 第一个轮廓\n }\n \n // 如果是新轮廓,添加起点\n if (isNewContour) {\n regionPoints.push(startX, startY)\n }\n \n if (isArc && arcParams) {\n // 离散化圆弧\n const { cx, cy, radius, startAngle, angleDiff } = arcParams\n const arcLength = Math.abs(angleDiff) * radius\n const arcSegments = Math.max(8, Math.ceil(arcLength / 0.5))\n \n for (let i = 1; i <= arcSegments; i++) {\n const t = i / arcSegments\n const angle = startAngle + angleDiff * t\n regionPoints.push(\n cx + Math.cos(angle) * radius,\n cy + Math.sin(angle) * radius\n )\n }\n } else {\n // 线段:添加终点\n regionPoints.push(endX, endY)\n }\n \n lastEnd = { x: endX, y: endY }\n })\n \n // 使用 earcut 三角化填充区域\n if (regionPoints.length >= 6) { // 至少3个点\n try {\n const triangles = earcut(regionPoints, holeIndices)\n \n if (triangles.length > 0) {\n const baseIndex = vertices.length / 3\n \n // 添加顶点\n for (let i = 0; i < regionPoints.length; i += 2) {\n vertices.push(regionPoints[i], regionPoints[i + 1], 0)\n }\n \n // 添加三角形索引\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n \n // 对于轮廓层,额外绘制描边(加粗轮廓线)\n if (isOutlineLayer && minWidth > 0) {\n const strokeRadius = minWidth / 2\n \n // 绘制轮廓边框(连接每个点)\n for (let i = 0; i < regionPoints.length - 2; i += 2) {\n const x1 = regionPoints[i]\n const y1 = regionPoints[i + 1]\n const x2 = regionPoints[i + 2]\n const y2 = regionPoints[i + 3]\n \n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n \n if (len > 0.001) {\n const nx = -dy / len * strokeRadius\n const ny = dx / len * strokeRadius\n \n // 线段主体(矩形)\n const v1 = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(v1, v1 + 1, v1 + 2)\n indices.push(v1 + 1, v1 + 3, v1 + 2)\n \n // 圆形端点\n const segments = Math.max(8, Math.ceil(strokeRadius * 4))\n \n // 起点圆\n if (i === 0) {\n const centerIdx = vertices.length / 3\n vertices.push(x1, y1, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x1 + Math.cos(angle) * strokeRadius, y1 + Math.sin(angle) * strokeRadius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 终点圆\n const centerIdx = vertices.length / 3\n vertices.push(x2, y2, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x2 + Math.cos(angle) * strokeRadius, y2 + Math.sin(angle) * strokeRadius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n }\n }\n } catch (error) {\n console.error(`[web-gerber] Region ${idx} 三角化失败:`, error)\n }\n }\n }\n })\n\n // 结束最后一个 polarity run(只要本次解析产生了 indices)\n if (indices.length > currentRunStart) {\n polarityRuns.push({ polarity: currentPolarity, start: currentRunStart, end: indices.length })\n }\n \n const verticesAdded = (vertices.length / 3) - verticesBefore\n const trianglesAdded = (indices.length / 3) - (verticesBefore > 0 ? indices.length / 3 - verticesBefore : 0)\n console.log(`[web-gerber] 补充提取: +${verticesAdded} 顶点, 总计 ${vertices.length / 3} 顶点, ${indices.length / 3} 三角形`)\n } // plotResult.children 处理结束\n \n // 最终统计\n console.log(`[web-gerber] ${fileName}: ${vertices.length / 3} 顶点, ${indices.length / 3} 三角形`)\n \n // 计算并输出坐标范围\n if (vertices.length > 0) {\n let minX = Infinity, maxX = -Infinity\n let minY = Infinity, maxY = -Infinity\n for (let i = 0; i < vertices.length; i += 3) {\n minX = Math.min(minX, vertices[i])\n maxX = Math.max(maxX, vertices[i])\n minY = Math.min(minY, vertices[i + 1])\n maxY = Math.max(maxY, vertices[i + 1])\n }\n console.log(`[web-gerber] ${fileName} 坐标范围: X=[${minX.toFixed(2)}, ${maxX.toFixed(2)}] mm, Y=[${minY.toFixed(2)}, ${maxY.toFixed(2)}] mm`)\n }\n \n return {\n vertices: new Float32Array(vertices),\n triangleIndices: new Uint32Array(indices),\n polarityRuns: polarityRuns.filter(r => (r.end - r.start) > 0),\n // 仅 drill 文件会有内容\n drillPosScaleRuns: drillPosScaleRuns && drillPosScaleRuns.length > 0 ? drillPosScaleRuns : null\n }\n } catch (error) {\n console.error(`[web-gerber] 解析失败:`, error)\n console.error(`[web-gerber] 错误堆栈:`, error.stack)\n throw error\n }\n}\n\n/**\n * 获取图层的显示名称\n * @param {string|Object} fileNameOrLayer - 文件名或图层对象\n */\nfunction getLayerDisplayName(fileNameOrLayer) {\n // 如果是图层对象,检查是否有存储的 phoLayerType\n if (typeof fileNameOrLayer === 'object' && fileNameOrLayer !== null) {\n if (fileNameOrLayer.phoLayerType) {\n return fileNameOrLayer.phoLayerType\n }\n fileNameOrLayer = fileNameOrLayer.name\n }\n \n const fileName = fileNameOrLayer\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // .gbr 文件:优先检查,根据文件名模式识别图层类型(必须在通用后缀匹配之前)\n if (fileExtension === '.GBR') {\n const nameUpper = fileNameOnly.toUpperCase()\n const nameLower = fileNameOnly.toLowerCase()\n\n // ✅ 关键补充:如果 basename 本身就是标准层名(例如 GTS.gbr / GTL.gbr / GKO.gbr),直接按层名识别\n const baseNameUpper = fileNameOnly.replace(/\\.[^.]+$/, '').toUpperCase()\n const directLayerNames = new Set([\n 'GTO', 'GTP', 'GTS', 'GTL',\n 'GBL', 'GBS', 'GBP', 'GBO',\n 'GKO',\n ])\n if (directLayerNames.has(baseNameUpper) || /^G\\d+$/i.test(baseNameUpper) || /^SIG\\d+$/i.test(baseNameUpper)) {\n return baseNameUpper\n }\n \n // legend_Top / Legend_Top -> GTO (优先匹配,使用更精确的匹配)\n if (nameLower.includes('legend_top') || \n (nameLower.includes('legend') && nameLower.includes('top') && !nameLower.includes('signal'))) {\n return 'GTO'\n }\n \n // Paste_Top -> GTP\n if (nameLower.includes('paste_top')) {\n return 'GTP'\n }\n \n // Paste_Bot -> GBP\n if (nameLower.includes('paste_bot')) {\n return 'GBP'\n }\n \n // soldermask_Top / Soldermask_Top -> GTS\n if (nameLower.includes('soldermask_top')) {\n return 'GTS'\n }\n \n // soldermask_Bot / Soldermask_Bot -> GBS\n if (nameLower.includes('soldermask_bot')) {\n return 'GBS'\n }\n \n // signal_1 -> SIG2, signal_2 -> SIG3, 依次类推 (包括 Copper_Signal_1, Copper_Signal_2)\n // 注意:这个匹配必须在 signal_Top/Bot 之前,避免误匹配\n const signalMatch = nameLower.match(/(?:copper_)?signal[_\\s](\\d+)/i)\n if (signalMatch) {\n const number = parseInt(signalMatch[1], 10)\n const displayNumber = number + 1\n return 'SIG' + displayNumber\n }\n \n // signal_Top -> GTL (包括 Copper_Signal_Top)\n if (nameLower.includes('signal_top') || nameLower.includes('copper_signal_top')) {\n return 'GTL'\n }\n \n // signal_Bot -> GBL (包括 Copper_Signal_Bot)\n if (nameLower.includes('signal_bot') || nameLower.includes('copper_signal_bot')) {\n return 'GBL'\n }\n \n // Profile -> GKO\n if (nameUpper.includes('PROFILE')) {\n return 'GKO'\n }\n\n // KiCad naming: supports underscore (v6+), dot (v5), and hyphen separators\n // e.g. project-F_Cu.gbr, led_blinker-F.Cu.gbr, board-Edge-Cuts.gbr\n if (/EDGE[._-]CUTS/i.test(nameUpper)) return 'GKO'\n if (/F[._-]CU\\b/i.test(nameUpper)) return 'GTL'\n if (/B[._-]CU\\b/i.test(nameUpper)) return 'GBL'\n if (/F[._-]MASK/i.test(nameUpper)) return 'GTS'\n if (/B[._-]MASK/i.test(nameUpper)) return 'GBS'\n if (/F[._-]SILKS?/i.test(nameUpper)) return 'GTO'\n if (/B[._-]SILKS?/i.test(nameUpper)) return 'GBO'\n if (/F[._-]PASTE/i.test(nameUpper)) return 'GTP'\n if (/B[._-]PASTE/i.test(nameUpper)) return 'GBP'\n if (/F[._-]FAB/i.test(nameUpper)) return 'GTO'\n if (/B[._-]FAB/i.test(nameUpper)) return 'GBO'\n // KiCad InN_Cu:第 N 个内层铜(与 signal_1→SIG2 一致:物理层号 = N+1,顶铜为第 1 层)\n if (/IN(\\d+)[._-]CU/i.test(nameUpper)) {\n const m = nameUpper.match(/IN(\\d+)[._-]CU/)\n return 'SIG' + (parseInt(m[1], 10) + 1)\n }\n\n // ZH-TOP -> GTS (保留兼容性)\n if (nameUpper.startsWith('ZH-TOP') || nameUpper.includes('ZH-TOP')) return 'GTS'\n // ZH-BOT -> GBS (保留兼容性)\n if (nameUpper.startsWith('ZH-BOT') || nameUpper.includes('ZH-BOT')) return 'GBS'\n // XL-TOP -> GTL (保留兼容性)\n if (nameUpper.startsWith('XL-TOP') || nameUpper.includes('XL-TOP')) return 'GTL'\n // XL-BOT -> GBL (保留兼容性)\n if (nameUpper.startsWith('XL-BOT') || nameUpper.includes('XL-BOT')) return 'GBL'\n\n // 检查是否包含特定关键词,如果包含则显示关键词部分\n const keywords = ['_Drawing', '_Drillmap', '_NPTH', '_Pads', '_PTH']\n for (const keyword of keywords) {\n if (fileNameOnly.includes(keyword)) {\n // 提取关键词及其后面的部分(包含扩展名)\n const keywordIndex = fileNameOnly.indexOf(keyword)\n return fileNameOnly.substring(keywordIndex)\n }\n }\n // 其他 .gbr 文件显示完整文件名(包含扩展名)\n return fileNameOnly\n }\n \n // 检查文件名后缀模式\n if (fileNameUpper.endsWith('_SST') || fileNameUpper.includes('_SST.')) return 'GTO'\n if (fileNameUpper.endsWith('_PMT') || fileNameUpper.includes('_PMT.')) return 'GTP'\n if (fileNameUpper.endsWith('_SMT') || fileNameUpper.includes('_SMT.')) return 'GTS'\n if (fileNameUpper.endsWith('_TOP') || fileNameUpper.includes('_TOP.')) return 'GTL'\n if (fileNameUpper.endsWith('_SMB') || fileNameUpper.includes('_SMB.')) return 'GBS'\n if (fileNameUpper.endsWith('_PMB') || fileNameUpper.includes('_PMB.')) return 'GBP'\n if (fileNameUpper.endsWith('_SSB') || fileNameUpper.includes('_SSB.')) return 'GBO'\n if (fileNameUpper.includes('OUTLINE')) return 'GKO'\n if (/.*_INT\\d+$/i.test(fileNameUpper) || /.*_INT\\d+\\./i.test(fileNameUpper)) return fileNameOnly\n \n // 检查扩展名\n if (fileExtension === '.D' || fileExtension === '.DRL' || fileExtension === '.DRI') {\n // 特殊命名:np.drl(非金属化孔)与 p.drl(金属化孔)\n if (fileNameUpper === 'NP.DRL' || fileNameUpper === 'NP.D') return 'DRL2'\n if (fileNameUpper === 'P.DRL' || fileNameUpper === 'P.D') return 'DRL'\n return 'DRL'\n }\n if (fileExtension === '.ROU') return 'rout'\n \n // CAM350 等软件导出的扩展名映射\n if (fileExtension === '.BOT') return 'GBL' // 底层铜\n if (fileExtension === '.TOP') return 'GTL' // 顶层铜\n if (fileExtension === '.SOB') return 'GBS' // 底层阻焊 (Solder mask Bottom)\n if (fileExtension === '.SOT') return 'GTS' // 顶层阻焊 (Solder mask Top)\n if (fileExtension === '.SST') return 'GTO' // 顶层丝印 (Silkscreen Top)\n if (fileExtension === '.SSB') return 'GBO' // 底层丝印 (Silkscreen Bottom)\n if (fileExtension === '.SMT') return 'GTS' // 顶层阻焊 (Solder Mask Top)\n if (fileExtension === '.SMB') return 'GBS' // 底层阻焊 (Solder Mask Bottom)\n if (fileExtension === '.PMT') return 'GTP' // 顶层锡膏 (Paste Mask Top)\n if (fileExtension === '.PMB') return 'GBP' // 底层锡膏 (Paste Mask Bottom)\n if (fileExtension === '.SER') return fileNameOnly // 丝印层,直接显示文件名\n if (fileExtension === '.ART') {\n // .art 文件:按文件名(不含扩展名)映射为标准层类型\n // SST -> GTO, SMT -> GTS, Top -> GTL, Bot -> GBL, SMB -> GBS, SSB -> GBO, OUT -> GKO\n const dot = fileNameOnly.lastIndexOf('.')\n const stemUpper = (dot > 0 ? fileNameOnly.substring(0, dot) : fileNameOnly).toUpperCase()\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 // 其他 .art:直接展示文件名+扩展名\n return fileNameOnly\n }\n \n // .pho 文件:根据文件名前缀和数字大小识别图层类型\n if (fileExtension === '.PHO') {\n const nameUpper = fileNameOnly.toUpperCase()\n \n // sst 开头 → GTO\n if (nameUpper.startsWith('SST')) {\n return 'GTO'\n }\n \n // smd 开头,需要判断数字大小\n if (nameUpper.startsWith('SMD')) {\n const numberMatch = nameUpper.match(/^SMD(\\d+)/)\n if (numberMatch) {\n const number = parseInt(numberMatch[1], 10)\n // 数字小(<20)→ GTP,数字大(>=20)→ GBP\n return number < 20 ? 'GTP' : 'GBP'\n } else {\n // 没有数字,默认按 GTP 处理\n return 'GTP'\n }\n }\n \n // sm 开头,需要判断数字大小\n if (nameUpper.startsWith('SM')) {\n const numberMatch = nameUpper.match(/^SM(\\d+)/)\n if (numberMatch) {\n const number = parseInt(numberMatch[1], 10)\n // 数字小(<20)→ GTS,数字大(>=20)→ GBS\n return number < 20 ? 'GTS' : 'GBS'\n } else {\n // 没有数字,默认按 GTS 处理\n return 'GTS'\n }\n }\n \n // art 开头,需要判断数字大小\n if (nameUpper.startsWith('ART')) {\n const numberMatch = nameUpper.match(/^ART(\\d+)/)\n if (numberMatch) {\n const number = parseInt(numberMatch[1], 10)\n // 数字小(<20)→ GTL,数字大(>=20)→ GBL\n return number < 20 ? 'GTL' : 'GBL'\n } else {\n // 没有数字,默认按 GTL 处理\n return 'GTL'\n }\n }\n \n // ssb 开头 → GBO\n if (nameUpper.startsWith('SSB')) {\n return 'GBO'\n }\n \n // drl 开头 → DRL\n if (nameUpper.startsWith('DRL')) {\n return 'DRL'\n }\n \n // dd 开头 → 显示完整文件名\n if (nameUpper.startsWith('DD')) {\n return fileNameOnly\n }\n \n // 其他 .pho 文件显示完整文件名\n return fileNameOnly\n }\n \n // GD+数字、GG+数字 显示完整文件名(包含扩展名)\n if (/^\\.GD\\d+$/i.test(fileExtension) || /^\\.GG\\d+$/i.test(fileExtension)) {\n return fileNameOnly\n }\n \n // GM 和 GM+数字(包括 GM1)显示完整文件名(包含扩展名)\n if (fileExtension === '.GM' || /^\\.GM\\d+$/i.test(fileExtension)) {\n return fileNameOnly\n }\n \n // INT+数字 显示完整文件名(包含扩展名)\n if (/^\\.INT\\d+$/i.test(fileExtension)) {\n return fileNameOnly\n }\n \n // G+数字 (.g2, .g3 ...) — KiCad/Protel 内层铜箔\n const gNumberMatch = fileExtension.match(/^\\.G(\\d+)$/i)\n if (gNumberMatch) {\n // KiCad: 文件名含 InN_Cu,显示名与内层序号一致(SIG(N+1)),不采用扩展名上的 .g2/.g3 编号\n const inCuMatch = fileNameUpper.match(/IN(\\d+)[._-]CU/i)\n if (inCuMatch) return 'SIG' + (parseInt(inCuMatch[1], 10) + 1)\n // Protel/Altium 惯例:.g1 = 第 1 内层 → SIG2\n const number = parseInt(gNumberMatch[1], 10)\n return 'SIG' + (number + 1)\n }\n \n // 如果是 GP+数字 格式,显示为 GP+数字\n const gpNumberMatch = fileExtension.match(/^\\.GP(\\d+)$/i)\n if (gpNumberMatch) {\n const number = parseInt(gpNumberMatch[1], 10)\n return 'GP' + number\n }\n \n // TXT 和 TX+数字 文件\n const isTxFile = /^\\.TX\\d*$/i.test(fileExtension) || fileExtension === '.TXT'\n if (isTxFile) {\n // 检查文件名中是否包含特定关键词\n if (/slotholes/i.test(fileName)) {\n return 'SLOT'\n }\n if (/[_\\s]via/i.test(fileName)) {\n return 'VIA'\n }\n // M48 格式的钻孔文件显示为 DRL\n // 注意:这里无法直接读取文件内容判断,但在图层列表中会通过 layer.isDrillFile 标记\n // 暂时显示扩展名,如果需要可以在 updateLayerList 中根据 layer.isDrillFile 动态修改\n return 'DRL'\n }\n \n // 默认返回扩展名(去掉点号)\n return fileExtension.substring(1)\n}\n\n/**\n * 获取文件的排序值\n */\nfunction getLayerOrder(fileName) {\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n const ext = fileExtension.substring(1) // 去掉点号\n \n const layerTypeOrder = {\n 'GTO': 0, 'GTP': 1, 'GTS': 2, 'GTL': 3, 'G1': 4, 'G2': 5,\n 'GBL': 6, 'GBS': 7, 'GBP': 7.5, 'GBO': 8, 'GKO': 9,\n 'SLOT': 9.5, 'DRL_TXT': 9.6, 'VIA': 11, 'rout': 11.5,\n 'GDD': 12, 'GDL': 13, 'GM': 14, 'GPB': 14.5, 'GPT': 15,\n 'DRL': 200,\n }\n \n // .gbr 文件:优先检查,根据文件名模式判断图层类型(必须在通用后缀匹配之前)\n if (ext === 'GBR') {\n const nameUpper = fileNameOnly.toUpperCase()\n const nameLower = fileNameOnly.toLowerCase()\n\n // ✅ 关键补充:如果 basename 本身就是标准层名(例如 GTS.gbr),直接使用对应 order\n const baseNameUpper = fileNameOnly.replace(/\\.[^.]+$/, '').toUpperCase()\n if (layerTypeOrder[baseNameUpper] !== undefined || /^G\\d+$/i.test(baseNameUpper) || /^SIG\\d+$/i.test(baseNameUpper)) {\n const order = layerTypeOrder[baseNameUpper] !== undefined ? layerTypeOrder[baseNameUpper] : 10\n console.log(`文件 ${fileName} 识别为 GBR->${baseNameUpper} (basename), order=${order}`)\n return order\n }\n \n // legend_Top -> GTO (order=0) (优先匹配,使用更精确的匹配)\n if (nameLower.includes('legend_top') || \n (nameLower.includes('legend') && nameLower.includes('top') && !nameLower.includes('signal'))) {\n console.log(`文件 ${fileName} 识别为 GBR->GTO (legend_Top), order=0`)\n return 0\n }\n \n // Paste_Top -> GTP (order=1)\n if (nameLower.includes('paste_top')) {\n console.log(`文件 ${fileName} 识别为 GBR->GTP (Paste_Top), order=1`)\n return 1\n }\n \n // Paste_Bot -> GBP (order=7.5)\n if (nameLower.includes('paste_bot')) {\n console.log(`文件 ${fileName} 识别为 GBR->GBP (Paste_Bot), order=7.5`)\n return 7.5\n }\n \n // soldermask_Top -> GTS (order=2)\n if (nameLower.includes('soldermask_top')) {\n console.log(`文件 ${fileName} 识别为 GBR->GTS (soldermask_Top), order=2`)\n return 2\n }\n \n // soldermask_Bot -> GBS (order=7)\n if (nameLower.includes('soldermask_bot')) {\n console.log(`文件 ${fileName} 识别为 GBR->GBS (soldermask_Bot), order=7`)\n return 7\n }\n \n // signal_1 -> SIG2 (order=3.1), signal_2 -> SIG3 (order=3.2), 依次类推 (包括 Copper_Signal_1, Copper_Signal_2)\n // 注意:这个匹配必须在 signal_Top/Bot 之前,避免误匹配\n const signalMatch = nameLower.match(/(?:copper_)?signal[_\\s](\\d+)/i)\n if (signalMatch) {\n const number = parseInt(signalMatch[1], 10)\n const order = Math.min(3 + number * 0.1, 5.9)\n console.log(`文件 ${fileName} 识别为 GBR->SIG${number + 1} (signal_${number}), order=${order}`)\n return order\n }\n \n // signal_Top -> GTL (order=3) (包括 Copper_Signal_Top)\n if (nameLower.includes('signal_top') || nameLower.includes('copper_signal_top')) {\n console.log(`文件 ${fileName} 识别为 GBR->GTL (signal_Top), order=3`)\n return 3\n }\n \n // signal_Bot -> GBL (order=6) (包括 Copper_Signal_Bot)\n if (nameLower.includes('signal_bot') || nameLower.includes('copper_signal_bot')) {\n console.log(`文件 ${fileName} 识别为 GBR->GBL (signal_Bot), order=6`)\n return 6\n }\n \n // Profile -> GKO (order=9)\n if (nameUpper.includes('PROFILE')) {\n console.log(`文件 ${fileName} 识别为 GBR->GKO (Profile), order=9`)\n return 9\n }\n\n // KiCad naming (underscore / dot / hyphen separators)\n if (/EDGE[._-]CUTS/i.test(nameUpper)) return 9 // GKO\n if (/F[._-]CU\\b/i.test(nameUpper)) return 3 // GTL\n if (/B[._-]CU\\b/i.test(nameUpper)) return 6 // GBL\n if (/F[._-]MASK/i.test(nameUpper)) return 2 // GTS\n if (/B[._-]MASK/i.test(nameUpper)) return 7 // GBS\n if (/F[._-]SILKS?/i.test(nameUpper)) return 0 // GTO\n if (/B[._-]SILKS?/i.test(nameUpper)) return 8 // GBO\n if (/F[._-]PASTE/i.test(nameUpper)) return 1 // GTP\n if (/B[._-]PASTE/i.test(nameUpper)) return 7.5 // GBP\n if (/F[._-]FAB/i.test(nameUpper)) return 0 // GTO\n if (/B[._-]FAB/i.test(nameUpper)) return 8 // GBO\n if (/IN(\\d+)[._-]CU/i.test(nameUpper)) {\n const m = nameUpper.match(/IN(\\d+)[._-]CU/)\n return Math.min(3 + parseInt(m[1], 10) * 0.1, 5.9)\n }\n\n // ZH-TOP -> GTS (Order 2) (保留兼容性)\n if (nameUpper.startsWith('ZH-TOP') || nameUpper.includes('ZH-TOP')) return 2\n // ZH-BOT -> GBS (Order 7) (保留兼容性)\n if (nameUpper.startsWith('ZH-BOT') || nameUpper.includes('ZH-BOT')) return 7\n // XL-TOP -> GTL (Order 3) (保留兼容性)\n if (nameUpper.startsWith('XL-TOP') || nameUpper.includes('XL-TOP')) return 3\n // XL-BOT -> GBL (Order 6) (保留兼容性)\n if (nameUpper.startsWith('XL-BOT') || nameUpper.includes('XL-BOT')) return 6\n \n // 其他未识别的 .gbr 文件(如 _Drawing, _Drillmap, _NPTH, _Pads, _PTH)排在 GKO 后面(order=10)\n console.log(`文件 ${fileName} 识别为 GBR (未识别), order=10`)\n return 10\n }\n \n // 检查文件名后缀\n if (fileNameUpper.endsWith('_SST') || fileNameUpper.includes('_SST.')) return 0\n if (fileNameUpper.endsWith('_PMT') || fileNameUpper.includes('_PMT.')) return 1\n if (fileNameUpper.endsWith('_SMT') || fileNameUpper.includes('_SMT.')) return 2\n if (fileNameUpper.endsWith('_TOP') || fileNameUpper.includes('_TOP.')) return 3\n if (fileNameUpper.endsWith('_SMB') || fileNameUpper.includes('_SMB.')) return 7\n if (fileNameUpper.endsWith('_PMB') || fileNameUpper.includes('_PMB.')) return 7.5\n if (fileNameUpper.endsWith('_SSB') || fileNameUpper.includes('_SSB.')) return 8\n if (fileNameUpper.includes('OUTLINE')) return 9\n \n // _INT+数字\n const intMatch = fileNameUpper.match(/_INT(\\d+)$/i) || fileNameUpper.match(/_INT(\\d+)\\./i)\n if (intMatch) {\n const number = parseInt(intMatch[1], 10)\n return 100 + number\n }\n \n // 扩展名检查\n // 特殊 DRL 命名:np.drl(DRL2)排在 p.drl(DRL)之后,且与 TXT 钻孔同一段落\n if ((ext === 'DRL' || ext === 'D') && fileNameOnly.toUpperCase() === 'NP.DRL') {\n return 9.65\n }\n if ((ext === 'DRL' || ext === 'D') && fileNameOnly.toUpperCase() === 'P.DRL') {\n return 9.6\n }\n if (layerTypeOrder[ext] !== undefined) {\n return layerTypeOrder[ext]\n }\n \n // KiCad InN_Cu + 扩展名 .g2/.g3:图层顺序以内层序号为准(与列表显示 SIG(N+1) 一致)\n const inCuForOrder = fileNameUpper.match(/IN(\\d+)[._-]CU/i)\n if (inCuForOrder && /^G\\d+$/i.test(ext)) {\n return Math.min(3 + parseInt(inCuForOrder[1], 10) * 0.1, 5.9)\n }\n\n // G+数字按照升序排在 GTL 和 GBL 之间(3.1-5.9)\n const gNumberMatch = ext.match(/^G(\\d+)$/i)\n if (gNumberMatch) {\n const number = parseInt(gNumberMatch[1], 10)\n return Math.min(3 + number * 0.1, 5.9)\n }\n \n // GP+数字排在 G+数字之后,GBL 之前(5.91-5.99)\n const gpNumberMatch = ext.match(/^GP(\\d+)$/i)\n if (gpNumberMatch) {\n const number = parseInt(gpNumberMatch[1], 10)\n return Math.min(5.9 + number * 0.01, 5.99)\n }\n \n // INT+数字(扩展名格式)\n const intNumberMatch = ext.match(/^INT(\\d+)$/i)\n if (intNumberMatch) {\n const number = parseInt(intNumberMatch[1], 10)\n return 100 + number\n }\n \n // GD+数字 排在 GDD (12) 和 GPB (14.5) 之间(14.1-14.4)\n const gdNumberMatch = ext.match(/^GD(\\d+)$/i)\n if (gdNumberMatch) {\n const number = parseInt(gdNumberMatch[1], 10)\n return Math.min(14 + number * 0.1, 14.4)\n }\n \n // GG+数字 排在 GDD (12) 和 GPB (14.5) 之间(14.1-14.4)\n const ggNumberMatch = ext.match(/^GG(\\d+)$/i)\n if (ggNumberMatch) {\n const number = parseInt(ggNumberMatch[1], 10)\n return Math.min(14 + number * 0.1, 14.4)\n }\n \n // GM+数字 排在 GM (14) 后面,GPB (14.5) 之前\n const gmNumberMatch = ext.match(/^GM(\\d+)$/i)\n if (gmNumberMatch) {\n const number = parseInt(gmNumberMatch[1], 10)\n if (number === 1) {\n // GM1 排在 GG1 之后,使用 14.2\n return 14.2\n } else {\n // GM2, GM3... 使用 14 + number * 0.1,最多到 14.4\n return Math.min(14 + number * 0.1, 14.4)\n }\n }\n \n // 检查是否是 SLOT(TXT 文件且文件名包含 SlotHoles)\n const isTxFile = /^TX\\d*$/i.test(ext) || ext === 'TXT'\n if (isTxFile && /slotholes/i.test(fileName)) {\n console.log(`文件 ${fileName} 识别为 SLOT, order=9.5`)\n return 9.5 // SLOT 在 GKO 之后\n }\n \n // 检查是否是 VIA(文件名包含 Via)\n if (/[_\\s]via/i.test(fileName) && isTxFile) {\n console.log(`文件 ${fileName} 识别为 VIA, order=11`)\n return 11\n }\n \n // TXT 文件(钻孔文件,但不是 SlotHoles 和 Via)\n if (isTxFile) {\n console.log(`文件 ${fileName} 识别为 DRL (TXT), order=9.6`)\n return 9.6 // DRL_TXT\n }\n \n // .art 文件:排在最后(order=100)\n if (ext === 'ART') {\n const dot = fileNameOnly.lastIndexOf('.')\n const stemUpper = (dot > 0 ? fileNameOnly.substring(0, dot) : fileNameOnly).toUpperCase()\n if (stemUpper === 'SST') return 0 // GTO\n if (stemUpper === 'SMT') return 2 // GTS\n if (stemUpper === 'TOP') return 3 // GTL\n if (stemUpper === 'BOT') return 6 // GBL\n if (stemUpper === 'SMB') return 7 // GBS\n if (stemUpper === 'SSB') return 8 // GBO\n if (stemUpper === 'OUT') return 9 // GKO\n console.log(`文件 ${fileName} 识别为 ART(unknown), order=100`)\n return 100\n }\n \n // .pho 文件:临时返回 100,后处理会根据同前缀文件的数字比较来更新 order\n // 后处理逻辑见 updatePhoLayerTypes 函数\n if (ext === 'PHO') {\n console.log(`文件 ${fileName} 识别为 PHO (临时order=100,后处理会更新)`)\n return 100\n }\n \n // .drl, .d, .dri 文件:钻孔文件,排在 200\n if (ext === 'DRL' || ext === 'D' || ext === 'DRI') {\n console.log(`文件 ${fileName} 识别为 DRL, order=200`)\n return 200\n }\n \n // .rou 文件:铣边文件,排在 11.5\n if (ext === 'ROU') {\n console.log(`文件 ${fileName} 识别为 ROU, order=11.5`)\n return 11.5\n }\n \n // CAM350 等软件导出的扩展名\n if (ext === 'BOT') return 6 // GBL\n if (ext === 'TOP') return 3 // GTL\n if (ext === 'SOB') return 7 // GBS (Solder mask Bottom)\n if (ext === 'SOT') return 2 // GTS (Solder mask Top)\n if (ext === 'SST') return 0 // GTO (Silkscreen Top)\n if (ext === 'SSB') return 8 // GBO (Silkscreen Bottom)\n if (ext === 'SMT') return 2 // GTS (Solder Mask Top)\n if (ext === 'SMB') return 7 // GBS (Solder Mask Bottom)\n if (ext === 'PMT') return 1 // GTP (Paste Mask Top)\n if (ext === 'PMB') return 7.5 // GBP (Paste Mask Bottom)\n \n // .ser 等直接显示文件名的图层,排在最后(order 1000)\n if (ext === 'SER') {\n console.log(`文件 ${fileName} 识别为 SER (显示文件名), order=1000`)\n return 1000\n }\n \n // 默认值:未识别的图层排在最后\n return 100\n}\n\nfunction animate() {\n render()\n requestAnimationFrame(animate)\n}\n\nfunction render() {\n // 使用纯黑背景,配合加法混合模式\n gl.clearColor(0.0, 0.0, 0.0, 1)\n // 同时清除颜色缓冲和模版缓冲\n gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)\n \n if (layers.length === 0) return\n \n gl.useProgram(program)\n gl.enableVertexAttribArray(positionLocation)\n \n // 启用模版测试\n gl.enable(gl.STENCIL_TEST)\n\n const aspect = canvas.width / canvas.height\n const matrix = [\n scale / aspect, 0, 0, 0,\n 0, scale, 0, 0,\n 0, 0, 1, 0,\n transX, transY, 0, 1\n ]\n gl.uniformMatrix4fv(uMatrixLocation, false, matrix)\n\n // --- Polarity (LPD/LPC) 支持 ---\n // Altium 等会在铜层里使用 %LPC*% / %LPD*% 表示“挖空/开窗”。\n // direct parser 会把每段 polarity 对应的 index 区间记录在 layer.polarityRuns 中。\n // 这里用 stencil 把 mask 做出来:dark 写 1,clear 写 0,然后用全屏 quad 按 stencil==1 上色。\n const layerHasClearPolarity = (layer) => {\n return Array.isArray(layer.polarityRuns) &&\n layer.polarityRuns.some(r => r && r.polarity === 'clear' && (r.end - r.start) > 0)\n }\n\n const drawLayer = (layer, blendFunc, alpha) => {\n if (layerHasClearPolarity(layer) && quadVertexBuffer && quadIndexBuffer) {\n // 1) 先构建 stencil mask(不写颜色)\n gl.clear(gl.STENCIL_BUFFER_BIT)\n gl.colorMask(false, false, false, false)\n gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)\n\n // 用当前世界矩阵绘制到 stencil\n gl.uniformMatrix4fv(uMatrixLocation, false, matrix)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, layer.vertexBuffer)\n gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, layer.indexBuffer)\n\n for (const run of layer.polarityRuns) {\n if (!run) continue\n const count = (run.end - run.start)\n if (count <= 0) continue\n const ref = run.polarity === 'clear' ? 0 : 1\n gl.stencilFunc(gl.ALWAYS, ref, 0xFF)\n gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_INT, run.start * 4)\n }\n\n // 2) 再根据 stencil==1 上色(用全屏 quad),clear 区域自然变“透明孔洞”\n gl.colorMask(true, true, true, true)\n gl.stencilFunc(gl.EQUAL, 1, 0xFF)\n gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP)\n\n gl.blendEquation(gl.FUNC_ADD)\n gl.blendFunc(...blendFunc)\n\n gl.uniform3fv(uColorLocation, layer.color)\n gl.uniform1f(uAlphaLocation, alpha)\n\n // quad 用 identity matrix(直接覆盖屏幕),由 stencil 负责裁剪\n gl.uniformMatrix4fv(uMatrixLocation, false, IDENTITY_MATRIX)\n gl.bindBuffer(gl.ARRAY_BUFFER, quadVertexBuffer)\n gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer)\n gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)\n\n // 恢复世界矩阵,避免影响后续 layer\n gl.uniformMatrix4fv(uMatrixLocation, false, matrix)\n return\n }\n\n // 默认路径:直接画几何\n gl.clear(gl.STENCIL_BUFFER_BIT)\n gl.stencilFunc(gl.NOTEQUAL, 1, 0xFF)\n gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)\n\n gl.blendEquation(gl.FUNC_ADD)\n gl.blendFunc(...blendFunc)\n\n gl.uniform3fv(uColorLocation, layer.color)\n gl.uniform1f(uAlphaLocation, alpha)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, layer.vertexBuffer)\n gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, layer.indexBuffer)\n gl.drawElements(gl.TRIANGLES, layer.vertexCount, gl.UNSIGNED_INT, 0)\n }\n \n // 渲染:大多数层按“叠加/正常混合”绘制。\n // 只有“开窗类”层(阻焊/锡膏)才使用差值/异或混合去表达“孔洞/镂空”的视觉效果。\n //\n // 重要:GBL/铜层不应使用差值混合,否则同一层内的重叠(三角化区域 + 线段/焊盘等)\n // 会被“异或”成黑色,导致你看到的“红底+黑色走线”的反相效果。\n // 差值/异或混合层:\n // - 阻焊/锡膏:用于“开窗/镂空”\n // - SIGn:用户指定需使用差值/异或(内层实验/对比用)\n //\n // 说明:GBL 不使用差值(否则会把其它层颜色“反相/抵消”,偏离常规Gerber Viewer 的堆叠观感)\n const diffBlendNames = ['GTS', 'GBS', 'GTP', 'GBP', 'GPT', 'GPB']\n const diffIndices = []\n const drillIndices = []\n const gdgggmIndices = []\n \n // 非差值层、非钻孔层\n for (let i = layers.length - 1; i >= 0; i--) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n \n const displayName = getLayerDisplayName(layer)\n const order = layer.order !== undefined ? layer.order : 100\n // GD/GG/GM+数字:\n // - GD/GG:作为对齐/说明类图层,保持置顶(在钻孔层之上)\n // - GM1:通常被当作轮廓/机械 1 层,保持置顶(历史行为)\n // - 其它 GMn(例如 GM15):用户期望在 GPT 之下,不应置顶覆盖锡膏层\n const nameUpper = layer.name.toUpperCase()\n const isGdNum = /\\.GD\\d+$/.test(nameUpper)\n const isGgNum = /\\.GG\\d+$/.test(nameUpper)\n const gmNumMatch = nameUpper.match(/\\.GM(\\d+)$/)\n const isGm1 = gmNumMatch && gmNumMatch[1] === '1'\n const isGdGgGmNum = isGdNum || isGgNum || isGm1\n if (isGdGgGmNum) {\n gdgggmIndices.push(i)\n continue\n }\n if (layer.isDrillFile || layer.isSlotFile) {\n drillIndices.push(i)\n continue\n }\n // SIGn:用户要求按“差值/异或”叠加(与其它软件保持一致)\n // 注意:这是基于 framebuffer dst 的颜色混合,会导致“勾选/取消底层(如GBL)改变结果”的现象,这是预期行为。\n const isSigInner = /^SIG\\d+$/i.test(displayName)\n const isDiffBlend = diffBlendNames.includes(displayName) || isSigInner\n if (isDiffBlend) {\n diffIndices.push(i)\n continue\n }\n \n const additiveNames = ['GTS', 'GBS', 'GTL', 'GBL', 'GPT', 'GBP', 'GTP', 'GPB']\n const isAdditiveLayer = additiveNames.includes(displayName) || (order >= 2.0 && order <= 15.0)\n const isSpecialLayer = (Math.abs(order - 0) < 0.1) || (Math.abs(order - 1) < 0.1)\n const alpha = 1.0\n \n let blendFunc = null\n if (isSpecialLayer) {\n blendFunc = [gl.ONE_MINUS_DST_COLOR, gl.ONE_MINUS_SRC_COLOR]\n } else if (isAdditiveLayer) {\n blendFunc = [gl.ONE, gl.ONE]\n } else {\n blendFunc = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA]\n }\n\n drawLayer(layer, blendFunc, alpha)\n }\n \n // 差值层单独绘制(差值/异或)\n for (const i of diffIndices.reverse()) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n const alpha = 1.0\n drawLayer(layer, [gl.ONE_MINUS_DST_COLOR, gl.ONE_MINUS_SRC_COLOR], alpha)\n }\n\n // 钻孔层单独绘制(置顶)\n for (const i of drillIndices.reverse()) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n const alpha = 1.0\n drawLayer(layer, [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA], alpha)\n }\n\n // GD/GG/GM+数字 层再绘制,置于钻孔层之上\n for (const i of gdgggmIndices.reverse()) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n const alpha = 1.0\n drawLayer(layer, [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA], alpha)\n }\n \n gl.disable(gl.STENCIL_TEST)\n}\n\n/**\n * 统一的图层排序函数\n * 对于 SIG 图层,按照 SIG 后面的数字大小排序(忽略 order)\n * 对于其他图层,按照 order 值排序\n */\nfunction sortLayersByOrder(layers) {\n return layers.map((layer, originalIndex) => ({\n layer,\n originalIndex,\n order: layer.order !== undefined ? layer.order : 100,\n displayName: getLayerDisplayName(layer)\n })).sort((a, b) => {\n // 检查是否是 SIG 图层\n const sigMatchA = a.displayName.match(/^SIG(\\d+)$/i)\n const sigMatchB = b.displayName.match(/^SIG(\\d+)$/i)\n \n // 如果两个都是 SIG 图层,直接按照 SIG 后面的数字大小排序(忽略 order)\n if (sigMatchA && sigMatchB) {\n const numA = parseInt(sigMatchA[1], 10)\n const numB = parseInt(sigMatchB[1], 10)\n return numA - numB\n }\n \n // 如果只有一个是 SIG 图层,按 order 排序(SIG 图层应该在 GTL 和 GBL 之间)\n if (sigMatchA || sigMatchB) {\n // 如果一个是 SIG,另一个不是,按 order 排序\n // SIG 图层的 order 应该在 3.1-5.9 范围内\n return a.order - b.order\n }\n \n // 如果都不是 SIG 图层,按 order 排序\n const orderDiff = a.order - b.order\n if (orderDiff !== 0) return orderDiff\n \n // 如果 order 相同且都不是 SIG,按原始索引排序(保持稳定性)\n return a.originalIndex - b.originalIndex\n })\n}\n\n/**\n * 根据 order 重新分配颜色\n * 确保按 order 排序后,第一个图层是红色(baseColors[0]),第二个是绿色(baseColors[1]),以此类推\n */\nfunction reassignColorsByOrder(layers) {\n // 使用统一的排序函数\n const sortedLayers = sortLayersByOrder(layers)\n \n // 按排序后的顺序重新分配颜色\n sortedLayers.forEach(({ layer, originalIndex }, colorIndex) => {\n const hexColor = pickColor(colorIndex)\n const rgb = hexToRgb(hexColor)\n \n // 更新图层的颜色属性\n layer.hexColor = hexColor\n layer.color = [rgb.r, rgb.g, rgb.b]\n \n // 更新 layerColorMap (使用原始索引)\n layerColorMap.set(originalIndex, hexColor)\n \n console.log(`[颜色重新分配] ${layer.name}: order=${layer.order}, originalIndex=${originalIndex}, colorIndex=${colorIndex}, color=${hexColor}`)\n })\n}\n\n/**\n * 更新 .pho 文件的图层类型\n * 根据同前缀文件的数字比较:数字最小的为\"小\"(上层),其他的为\"大\"(下层)\n */\nfunction updatePhoLayerTypes(layers) {\n // 收集所有 .pho 文件\n const phoFiles = layers.filter(layer => {\n const fileNameOnly = (layer.name || '').split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n return fileExtension === '.PHO'\n })\n \n if (phoFiles.length === 0) return\n \n // 按前缀分组\n const prefixGroups = {}\n for (const layer of phoFiles) {\n const fileNameOnly = (layer.name || '').split(/[/\\\\]/).pop()\n const nameUpper = fileNameOnly.toUpperCase()\n \n // 提取前缀和数字\n let prefix = null\n let number = null\n \n if (nameUpper.startsWith('SST')) {\n prefix = 'sst'\n const match = nameUpper.match(/^SST(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('SMD')) {\n prefix = 'smd'\n const match = nameUpper.match(/^SMD(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('SM')) {\n prefix = 'sm'\n const match = nameUpper.match(/^SM(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('ART')) {\n prefix = 'art'\n const match = nameUpper.match(/^ART(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('SSB')) {\n prefix = 'ssb'\n number = 0 // ssb 不需要比较数字\n } else if (nameUpper.startsWith('DRL')) {\n prefix = 'drl'\n number = 0 // drl 不需要比较数字\n } else if (nameUpper.startsWith('DD')) {\n prefix = 'dd'\n number = 0 // dd 不需要比较数字\n }\n \n if (prefix) {\n if (!prefixGroups[prefix]) {\n prefixGroups[prefix] = []\n }\n prefixGroups[prefix].push({ layer, number, fileNameOnly })\n }\n }\n \n // 对每个前缀组进行处理\n for (const [prefix, files] of Object.entries(prefixGroups)) {\n // 按数字排序\n files.sort((a, b) => a.number - b.number)\n \n // 数字最小的为\"小\"(上层),其他的为\"大\"(下层)\n for (let i = 0; i < files.length; i++) {\n const { layer, number } = files[i]\n const isSmallest = i === 0\n \n let layerType = null\n let order = null\n \n switch (prefix) {\n case 'sst':\n // sst 开头 → GTO (order=0)\n layerType = 'GTO'\n order = 0\n break\n case 'smd':\n // smd 开头,数字最小 → GTP (order=1),其他 → GBP (order=7.5)\n if (isSmallest) {\n layerType = 'GTP'\n order = 1\n } else {\n layerType = 'GBP'\n order = 7.5\n }\n break\n case 'sm':\n // sm 开头,数字最小 → GTS (order=2),其他 → GBS (order=7)\n if (isSmallest) {\n layerType = 'GTS'\n order = 2\n } else {\n layerType = 'GBS'\n order = 7\n }\n break\n case 'art':\n // art 开头,数字最小 → GTL (order=3),其他 → GBL (order=6)\n if (isSmallest) {\n layerType = 'GTL'\n order = 3\n } else {\n layerType = 'GBL'\n order = 6\n }\n break\n case 'ssb':\n // ssb 开头 → GBO (order=8)\n layerType = 'GBO'\n order = 8\n break\n case 'drl':\n // drl 开头 → DRL (order=200)\n layerType = 'DRL'\n order = 200\n break\n case 'dd':\n // dd 开头 → 显示完整文件名 (order=201,排在 DRL 后面)\n // 不设置 layerType,让 getLayerDisplayName 返回完整文件名\n layerType = null\n order = 201\n break\n }\n \n if (order !== null) {\n // 更新 order\n layer.order = order\n // 如果有 layerType,存储图层类型\n if (layerType) {\n layer.phoLayerType = layerType\n console.log(`[PHO图层识别] ${layer.name}: 前缀=${prefix}, 数字=${number}, 类型=${layerType}, order=${order}`)\n } else {\n console.log(`[PHO图层识别] ${layer.name}: 前缀=${prefix}, 数字=${number}, 显示完整文件名, order=${order}`)\n }\n }\n }\n }\n}\n\n// --- File Handling ---\n\nfileInput.addEventListener('change', async (e) => {\n const file = e.target.files[0]\n if (!file) return\n \n showStatus(`Processing ${file.name}...`, 'loading')\n showFileInfo(file.name, file.size)\n showLoading()\n \n let filesToProcess = []\n const ext = '.' + file.name.split('.').pop().toLowerCase()\n \n try {\n if (ext === '.zip') {\n filesToProcess = await extractZip(file)\n } else if (ext === '.rar') {\n filesToProcess = await extractRar(file)\n } else {\n filesToProcess = [file]\n }\n \n if (filesToProcess.length === 0) {\n showStatus('No Gerber files found.', 'error')\n hideLoading()\n return\n }\n \n // 按照 order 排序文件\n filesToProcess.sort((a, b) => getLayerOrder(a.name) - getLayerOrder(b.name))\n \n // 清空图层和 solo 模式\n layers = []\n layerList.innerHTML = ''\n layerColorMap.clear()\n isSoloMode = false\n soloLayerOrder = []\n soloTargetLayerIndex = null\n cacheNeedsUpdate = true\n \n // 强制重绘一次以清空画布\n render() \n \n let colorIndex = 0 // 颜色索引,只为成功解析的文件分配\n const pendingLayers = [] // 临时存储解析好的图层,避免解析过程中频繁刷新导致画面跳动\n \n // 处理所有文件\n for (let i = 0; i < filesToProcess.length; i++) {\n const f = filesToProcess[i]\n showStatus(`Parsing ${i+1}/${filesToProcess.length}: ${f.name}...`, 'loading')\n \n // 检查文件类型\n const fileExt = '.' + f.name.split('.').pop().toLowerCase()\n \n // 读取文件内容\n const text = await f.text()\n \n // 从 Gerber 文件提取 %FS/%MO 作为后续 Excellon(TXT/DRL) 的格式兜底\n // 注意:函数内部会自动跳过包含 M48 的钻孔文件\n updateInheritedGerberFormatHintFromText(text, f.name)\n \n // 对于 TXT 和 TX+数字 文件,检查是否是 Excellon 格式(M48 开头)\n const isTxFile = /^\\.tx\\d*$/i.test(fileExt) || fileExt === '.txt'\n const isGbrFile = fileExt === '.gbr'\n const isPhoFile = fileExt === '.pho'\n const isArtFile = fileExt === '.art'\n const isDrlFile = fileExt === '.drl' || fileExt === '.d' || fileExt === '.dri'\n const isRouFile = fileExt === '.rou'\n let isM48DrillFile = false\n let isSlotFile = false\n let useWebGerber = false\n let useDirectParser = false\n \n if (isTxFile) {\n // 检查是否是 SlotHoles 文件\n if (/slotholes/i.test(f.name)) {\n isSlotFile = true\n console.log(`[SLOT] ${f.name} 是 SlotHoles 文件`)\n }\n \n // 检查前10行是否包含 M48(Excellon 钻孔文件头)\n // 有些文件第一行是 M72(英制单位)或其他指令,M48 在第二行\n const lines = text.trim().split('\\n').slice(0, 10)\n const hasM48 = lines.some(line => /^M48$/i.test(line.trim()))\n \n if (hasM48) {\n isM48DrillFile = true\n useWebGerber = true\n if (isSlotFile) {\n console.log(`[SLOT] ${f.name} 是 Excellon SlotHoles 文件(含M48),使用 web-gerber 解析`)\n } else {\n console.log(`[钻孔] ${f.name} 是 Excellon 钻孔文件(含M48),使用 web-gerber 解析`)\n }\n } else {\n console.log(`[Gerber] ${f.name} 是 TXT 文件但不含 M48,使用 web-gerber 解析`)\n useWebGerber = true\n }\n } else if (isGbrFile) {\n // .gbr 文件使用 web-gerber 解析\n console.log(`[Gerber] ${f.name} 是 GBR 文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (isPhoFile) {\n // .pho 文件使用 web-gerber 解析\n console.log(`[Gerber] ${f.name} 是 PHO 文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (isArtFile) {\n // .art 文件使用 web-gerber 解析\n console.log(`[Gerber] ${f.name} 是 ART 文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (isDrlFile) {\n // .drl 和 .d 文件使用 web-gerber 解析(Excellon 钻孔文件)\n console.log(`[钻孔] ${f.name} 是 DRL 钻孔文件,使用 web-gerber 解析`)\n isM48DrillFile = true\n useWebGerber = true\n } else if (isRouFile) {\n // .rou 文件使用 web-gerber 解析(铣边文件)\n console.log(`[铣边] ${f.name} 是 ROU 铣边文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else {\n // CAM350 等软件导出的扩展名,使用 web-gerber 解析\n const isCam350File = ['.bot', '.top', '.sob', '.sot', '.ser', '.sst', '.ssb', '.smt', '.smb', '.pmt', '.pmb'].includes(fileExt)\n if (isCam350File) {\n console.log(`[Gerber] ${f.name} 是 CAM350 格式文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else {\n // 对于标准 Gerber 文件,检测是否包含宏定义或区域填充\n // 如果包含宏定义(%AM...%)或区域填充(G36),强制使用 web-gerber\n const hasMacroDefinition = /%AM[A-Z0-9_]+\\*/i.test(text)\n const hasRegionFill = /G36\\*/i.test(text) // G36 开始区域填充\n if (hasMacroDefinition) {\n console.log(`[宏定义] ${f.name} 包含 Aperture Macro 定义,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (hasRegionFill) {\n console.log(`[区域填充] ${f.name} 包含 G36 区域填充命令,使用 web-gerber 解析`)\n useWebGerber = true\n }\n }\n }\n \n try {\n let data\n \n // 准备解析选项 - 检查文件扩展名和文件名判断轮廓层\n const fileNameOnly = f.name.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // 检查扩展名和文件名关键词\n const isOutlineLayer = \n getLayerDisplayName(f.name) === 'GKO' ||\n fileExtension === '.GKO' || \n fileExtension === '.GM' || \n /^\\.GM\\d+$/i.test(fileExtension) ||\n fileNameUpper.includes('OUTLINE') ||\n /EDGE[._-]CUTS/i.test(fileNameUpper) ||\n fileNameUpper.includes('WX') ||\n fileNameUpper.includes('BOARD') ||\n fileNameUpper.startsWith('GKO') ||\n fileNameUpper.startsWith('OUT')\n \n const parserOpts = {\n minLineWidth: isOutlineLayer ? 0.3 : 0 // 轮廓层(GKO、GM1等)最小线宽 0.3mm\n }\n \n // 只在识别为轮廓层时打印日志\n if (isOutlineLayer) {\n console.log(`[轮廓层] ${fileNameOnly}, minLineWidth=${parserOpts.minLineWidth}mm`)\n }\n \n if (useWebGerber) {\n data = await parseWithWebGerber(text, f.name)\n // web-gerber 返回空几何时,对非钻孔文件回退到 GerberDirectParser\n if ((!data.vertices || data.vertices.length === 0) && !isM48DrillFile) {\n console.log(`[回退] ${f.name} web-gerber 无几何数据,尝试 GerberDirectParser`)\n const parser = new GerberDirectParser(parserOpts)\n data = parser.parse(text)\n console.log(`[回退结果] ${f.name}: ${data.vertices.length / 3} 顶点, ${data.triangleIndices.length / 3} 三角形`)\n }\n } else if (useDirectParser) {\n console.log(`[直接解析] 开始解析 ${f.name}`)\n const parser = new GerberDirectParser(parserOpts)\n data = parser.parse(text)\n console.log(`[直接解析] ${f.name}: ${data.vertices.length / 3} 顶点, ${data.triangleIndices.length / 3} 三角形`)\n } else {\n const parser = new GerberDirectParser(parserOpts)\n data = parser.parse(text)\n }\n \n // 检查是否有几何数据\n if (!data.vertices || data.vertices.length === 0) {\n console.log(`[跳过] ${f.name} 没有几何数据`)\n continue\n }\n \n // 对于参考层(GTS/GBS/GTL/GBL),额外用web-gerber解析一次以提取特征点\n let featureExtractionVertices = null\n const refLayerFileName = f.name.split(/[/\\\\]/).pop()\n const refLayerExt = '.' + refLayerFileName.split('.').pop().toUpperCase()\n const refLayerExtName = refLayerExt.substring(1)\n // 使用 getLayerDisplayName 判断逻辑层类型(支持 .pho/.art 等文件名映射)\n const logicalLayerType = getLayerDisplayName(f.name)\n const isRefLayer = ['GTS', 'GBS', 'GTL', 'GBL'].includes(refLayerExtName) || ['GTS', 'GBS', 'GTL', 'GBL'].includes(logicalLayerType)\n \n if (isRefLayer && !useWebGerber) {\n console.log(`[特征提取] ${f.name} (${refLayerExtName}) 额外用web-gerber解析以提取圆心`)\n try {\n // 临时抑制web-gerber的详细日志\n const originalLog = console.log\n const silencedPatterns = ['[web-gerber]', 'plotResult', 'SVG', 'Child']\n console.log = (...args) => {\n const message = args.join(' ')\n if (!silencedPatterns.some(pattern => message.includes(pattern))) {\n originalLog.apply(console, args)\n }\n }\n \n const featureData = await parseWithWebGerber(text, f.name)\n \n // 恢复console.log\n console.log = originalLog\n \n if (featureData && featureData.vertices && featureData.vertices.length > 0) {\n featureExtractionVertices = featureData.vertices\n console.log(`[特征提取] ${f.name} ✓ 提取到 ${featureExtractionVertices.length / 3} 个顶点用于圆心识别`)\n }\n } catch (err) {\n console.log = console.log.bind(console) // 确保恢复\n console.warn(`[特征提取] ${f.name} ✗ web-gerber解析失败:`, err.message)\n }\n }\n \n // 计算当前数据的边界\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n for (let k = 0; k < data.vertices.length; k += 3) {\n const x = data.vertices[k]\n const y = data.vertices[k + 1]\n minX = Math.min(minX, x)\n maxX = Math.max(maxX, x)\n minY = Math.min(minY, y)\n maxY = Math.max(maxY, y)\n }\n const bounds = { minX, minY, maxX, maxY }\n\n // 根据成功解析的文件顺序分配颜色\n // 当颜色用完后,后续图层都使用白色\n const hexColor = pickColor(colorIndex)\n const rgb = hexToRgb(hexColor)\n colorIndex++\n \n // 创建独立的 WebGL 缓冲\n const vertexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, data.vertices, gl.STATIC_DRAW)\n \n const indexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.triangleIndices, gl.STATIC_DRAW)\n \n // 决定保存哪些顶点数据用于特征提取\n // 优先级:特征提取专用顶点 > 参考层(直接用web-gerber结果) > 渲染顶点(钻孔层)\n let rawVerticesForFeatureExtraction = null\n if (featureExtractionVertices) {\n // 参考层:使用web-gerber生成的特征提取专用顶点\n rawVerticesForFeatureExtraction = featureExtractionVertices\n } else if (isRefLayer && useWebGerber) {\n // 参考层:如果本身就是用 web-gerber 解析的,也需要保留一份顶点用于圆心特征提取/钻孔对齐\n // parseWithWebGerber 已做了 Y 翻转到“世界坐标系(Y向上)”并对 circle 额外注入中心点,适合作为特征输入\n rawVerticesForFeatureExtraction = data.vertices\n } else if (isM48DrillFile || isDrlFile) {\n // 钻孔层:使用渲染顶点\n rawVerticesForFeatureExtraction = data.vertices\n }\n \n pendingLayers.push({\n name: f.name,\n visible: true,\n color: [rgb.r, rgb.g, rgb.b],\n hexColor: hexColor,\n // vertices: Array.from(data.vertices), // 不再保留 CPU 端数据以节省内存\n // indices: Array.from(data.triangleIndices),\n vertexBuffer,\n indexBuffer,\n vertexCount: data.triangleIndices.length,\n // Gerber LP(LPC/LPD)极性段落:用于 stencil 挖空(主要影响铜层大面积铺铜的“避空/开窗”)\n polarityRuns: data.polarityRuns || null,\n // Drill:记录每个孔(或中心+尺寸形状)的顶点区间,用于后续“只缩放孔位、不缩放孔径”的修正\n drillPosScaleRuns: data.drillPosScaleRuns || null,\n order: getLayerOrder(f.name),\n isDrillFile: isM48DrillFile,\n isSlotFile: isSlotFile,\n bounds: bounds,\n // rawVertices用于特征提取(钻孔层用渲染顶点,参考层用web-gerber生成的专用顶点)\n rawVertices: rawVerticesForFeatureExtraction\n })\n \n console.log(`[成功] ${f.name}: ${data.vertices.length / 3} 顶点, ${data.triangleIndices.length / 3} 三角形`)\n \n // 注意:不再立即更新 layers 和 UI,等所有文件解析完再一次性更新\n // 这样可以避免画面跳动和频繁重绘\n \n } catch (error) {\n console.error(`[错误] 解析文件 ${f.name} 失败:`, error.message)\n // 继续处理下一个文件\n }\n \n // 让浏览器有机会更新 UI(显示解析进度)\n if (i < filesToProcess.length - 1) {\n await new Promise(resolve => setTimeout(resolve, 0))\n }\n }\n \n if (pendingLayers.length === 0) {\n showStatus('No geometry generated.', 'error')\n hideLoading()\n return\n }\n \n // 所有文件解析完成,赋值给 layers\n layers = pendingLayers\n \n // 后处理:根据同前缀文件的数字比较来更新 .pho 文件的图层类型\n updatePhoLayerTypes(layers)\n \n // 根据更新后的 order 重新分配颜色,确保第一个图层(order最小)是红色\n reassignColorsByOrder(layers)\n \n // 在显示之前,统一进行钻孔层对齐修正\n // 此时所有图层的 bounds 都已知,可以准确找到参考层\n checkDrillLayerAlignment()\n \n // 计算总三角形数\n const totalTris = layers.reduce((sum, l) => sum + l.vertexCount / 3, 0)\n showStatus(`✓ Rendered ${layers.length} layers (${Math.floor(totalTris)} tris)`, 'success')\n hideLoading()\n \n // 更新 UI 列表\n updateLayerList()\n \n // 标记缓存需要更新并自动居中\n cacheNeedsUpdate = true\n centerView()\n \n } catch (err) {\n console.error(err)\n showStatus('✗ Error: ' + err.message, 'error')\n hideLoading()\n }\n})\n\nfunction showStatus(msg, type = '') {\n status.textContent = msg\n status.className = `status ${type}`\n}\n\nfunction showFileInfo(name, size) {\n fileName.textContent = name\n fileSize.textContent = (size / 1024).toFixed(2) + ' KB'\n fileInfo.classList.add('active')\n}\n\nfunction updateLayerList() {\n layerList.innerHTML = ''\n \n // 使用统一的排序函数\n const sortedLayers = sortLayersByOrder(layers)\n \n // 调试:打印排序后的图层顺序\n console.log('[图层列表排序] 排序后的图层顺序:')\n sortedLayers.forEach(({ layer, originalIndex }, index) => {\n const displayName = getLayerDisplayName(layer)\n console.log(` ${index + 1}. ${displayName} (order=${layer.order || 100}, originalIndex=${originalIndex})`)\n })\n \n sortedLayers.forEach(({ layer, originalIndex }, displayIndex) => {\n const item = document.createElement('div')\n item.className = layer.visible ? 'layer-item' : 'layer-item disabled'\n \n const indexSpan = document.createElement('span')\n indexSpan.className = 'layer-index'\n indexSpan.textContent = displayIndex + 1\n \n const toggle = document.createElement('div')\n toggle.className = `layer-toggle ${layer.visible ? 'checked' : ''}`\n toggle.onclick = (e) => {\n e.stopPropagation()\n toggleLayer(originalIndex) // 使用原始索引\n }\n \n const colorBox = document.createElement('div')\n colorBox.className = 'layer-color'\n if (!layer.visible) {\n colorBox.style.backgroundColor = '#cccccc' // 浅灰色\n } else {\n let displayColor = layer.hexColor\n if (layerColorMap.has(originalIndex)) { // 使用原始索引\n displayColor = layerColorMap.get(originalIndex)\n }\n colorBox.style.backgroundColor = displayColor\n }\n \n const nameSpan = document.createElement('div')\n nameSpan.className = 'layer-name'\n let displayName = getLayerDisplayName(layer)\n // 如果是 SLOT 文件,显示 SLOT\n if (layer.isSlotFile) {\n displayName = 'SLOT'\n }\n // 如果是钻孔文件,根据文件名显示特定名称\n else if (layer.isDrillFile) {\n const fileName = layer.name\n if (/[_\\s]via/i.test(fileName)) {\n displayName = 'VIA'\n } else {\n displayName = 'DRL'\n }\n }\n nameSpan.textContent = displayName\n nameSpan.setAttribute('title', layer.name)\n \n // 双击图层名称:单独显示该图层\n nameSpan.addEventListener('dblclick', (e) => {\n e.stopPropagation()\n soloLayer(originalIndex) // 使用原始索引\n })\n \n item.appendChild(indexSpan)\n item.appendChild(toggle)\n item.appendChild(colorBox)\n item.appendChild(nameSpan)\n layerList.appendChild(item)\n })\n \n updateToggleAllState()\n}\n\n/**\n * 切换图层可见性\n */\nfunction toggleLayer(index) {\n if (index < 0 || index >= layers.length) return\n \n layers[index].visible = !layers[index].visible\n \n // 如果处于 solo 模式\n if (isSoloMode) {\n if (layers[index].visible) {\n // 显示图层:按勾选顺序添加\n if (soloLayerOrder.indexOf(index) === -1) {\n soloLayerOrder.push(index)\n \n // 分配颜色\n if (layerColorMap.has(index)) {\n const existingColor = layerColorMap.get(index)\n updateLayerColor(index, existingColor)\n } else {\n const colorIndex = layerColorMap.size\n const newColor = pickColor(colorIndex)\n layerColorMap.set(index, newColor)\n updateLayerColor(index, newColor)\n }\n } else {\n if (layerColorMap.has(index)) {\n const existingColor = layerColorMap.get(index)\n updateLayerColor(index, existingColor)\n }\n }\n }\n }\n \n updateLayerList()\n cacheNeedsUpdate = true\n}\n\n/**\n * 单独显示图层\n */\nfunction soloLayer(targetLayerIndex) {\n console.log(`单独显示图层 ${targetLayerIndex}`)\n \n // 进入单独显示模式\n isSoloMode = true\n \n // 清空颜色映射和勾选顺序数组\n layerColorMap.clear()\n soloLayerOrder = []\n \n // 隐藏所有图层\n layers.forEach((layer, index) => {\n layer.visible = false\n })\n \n // 只显示目标图层\n layers[targetLayerIndex].visible = true\n \n // 将目标图层添加到勾选顺序数组\n soloLayerOrder.push(targetLayerIndex)\n \n // 保存双击的目标图层索引\n soloTargetLayerIndex = targetLayerIndex\n \n // 目标图层使用 baseColors[0](红色)\n const newColor = baseColors[0]\n layerColorMap.set(targetLayerIndex, newColor)\n updateLayerColor(targetLayerIndex, newColor)\n \n // 更新图层列表\n updateLayerList()\n cacheNeedsUpdate = true\n \n console.log(`图层 ${targetLayerIndex} 颜色重置为: ${newColor}`)\n}\n\n/**\n * 更新图层颜色\n */\nfunction updateLayerColor(layerIndex, hexColor) {\n if (layerIndex < 0 || layerIndex >= layers.length) return\n \n const layer = layers[layerIndex]\n const rgb = hexToRgb(hexColor)\n \n // 更新图层的颜色数组(注意这会直接修改原始顶点颜色)\n layer.color = [rgb.r, rgb.g, rgb.b]\n layer.hexColor = hexColor\n}\n\nlayerToggleAll.onclick = () => {\n if (layers.length === 0) return\n const allVisible = layers.every(l => l.visible)\n const newVisible = !allVisible\n \n // 如果处于 solo 模式且是全部显示操作\n if (isSoloMode && !allVisible) {\n // 按照排序顺序(order 值从小到大)重新分配颜色\n const sortedLayers = layers.map((layer, idx) => ({ layer, idx }))\n .sort((a, b) => (a.layer.order || 100) - (b.layer.order || 100))\n \n // 清空并重新填充 soloLayerOrder\n soloLayerOrder = []\n \n // 颜色分配索引(从1开始,因为双击的图层使用baseColors[0])\n let colorIndex = 1\n \n // 按照排序顺序依次分配颜色\n sortedLayers.forEach(({ layer, idx }) => {\n layer.visible = true\n \n // 如果是双击的目标图层,使用 baseColors[0](红色)\n if (idx === soloTargetLayerIndex) {\n const newColor = baseColors[0]\n layerColorMap.set(idx, newColor)\n updateLayerColor(idx, newColor)\n soloLayerOrder.push(idx)\n } else {\n // 其他图层按照排序顺序从 baseColors[1] 开始分配颜色\n const newColor = pickColor(colorIndex)\n layerColorMap.set(idx, newColor)\n updateLayerColor(idx, newColor)\n soloLayerOrder.push(idx)\n colorIndex++\n }\n })\n \n console.log('solo模式下全部显示,双击的图层保持红色,其余图层按排序顺序分配颜色')\n } else {\n // 非 solo 模式或全部隐藏操作\n layers.forEach(layer => {\n layer.visible = newVisible\n })\n }\n \n updateLayerList()\n cacheNeedsUpdate = true\n}\n\nfunction updateToggleAllState() {\n const allVisible = layers.every(l => l.visible)\n if (allVisible) {\n layerToggleAllCheckbox.classList.add('checked')\n } else {\n layerToggleAllCheckbox.classList.remove('checked')\n }\n}\n\nasync function extractZip(file) {\n const files = []\n try {\n const zip = await JSZip.loadAsync(file)\n for (const [filename, zipEntry] of Object.entries(zip.files)) {\n if (!zipEntry.dir && isGerberFile(filename)) {\n const blob = await zipEntry.async('blob')\n files.push(new File([blob], filename))\n }\n }\n } catch (e) {\n console.error(e)\n throw new Error('Failed to unzip')\n }\n return files\n}\n\nfunction isGerberFile(fileName) {\n // 提取文件名(去掉路径,只保留文件名部分)\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // 先检查文件名后缀模式(优先级最高)\n const specialSuffixes = ['_SST', '_PMT', '_SMT', '_TOP', '_SMB', '_PMB', '_SSB', 'OUTLINE']\n for (const suffix of specialSuffixes) {\n if (fileNameUpper.endsWith(suffix) || fileNameUpper.includes(suffix + '.')) {\n return true\n }\n }\n \n // 检查 _INT+数字 模式\n if (/.*_INT\\d+$/i.test(fileNameUpper) || /.*_INT\\d+\\./i.test(fileNameUpper)) {\n return true\n }\n \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 // 新增:CAM350 等软件导出的扩展名\n '.bot', '.top', '.sob', '.sot', '.dri', '.ser',\n '.sst', '.ssb', '.smt', '.smb', '.pmt', '.pmb'\n ]\n const fileExtension = '.' + fileNameOnly.split('.').pop().toLowerCase()\n return validExtensions.includes(fileExtension) || \n /^\\.g\\d+$/i.test(fileExtension) || \n /^\\.gm\\d+$/i.test(fileExtension) ||\n /^\\.gp\\d+$/i.test(fileExtension) ||\n /^\\.tx\\d+$/i.test(fileExtension) ||\n /^\\.int\\d+$/i.test(fileExtension)\n}\n\n/**\n * 检测和修正钻孔层错位\n * 在所有图层解析完成后调用,确保参考层可用\n */\nfunction checkDrillLayerAlignment() {\n // 查找钻孔层\n const drillLayers = layers.filter(layer => layer.isDrillFile || layer.isSlotFile)\n \n if (drillLayers.length === 0) {\n console.log('[钻孔对齐] 未找到钻孔层,跳过')\n return\n }\n \n // 组合钻孔层边界(用于 sanity check / GKO fallback)\n const combineBounds = (layersToCombine) => {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const l of layersToCombine) {\n if (!l || !l.bounds) continue\n minX = Math.min(minX, l.bounds.minX)\n minY = Math.min(minY, l.bounds.minY)\n maxX = Math.max(maxX, l.bounds.maxX)\n maxY = Math.max(maxY, l.bounds.maxY)\n }\n if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {\n return null\n }\n return { minX, minY, maxX, maxY }\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 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 // 查找GKO(或 outline)层,用于校验/兜底对齐\n const outlineLayer = layers.find(layer => {\n const fileNameOnly = (layer.name || '').split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const upper = fileNameOnly.toUpperCase()\n // 兼容:有些工程没有 GKO,轮廓在 GM1/GMxx(Mechanical)里\n return ext === 'GKO' ||\n ext === 'GM' ||\n /^GM\\d+$/i.test(ext) ||\n upper.includes('_PROFILE') ||\n upper.includes('PROFILE') ||\n upper.includes('OUTLINE') ||\n upper.includes('BOARD') ||\n upper.startsWith('GKO')\n })\n\n let outlineBounds = outlineLayer && outlineLayer.bounds ? outlineLayer.bounds : null\n let combinedDrillBounds = combineBounds(drillLayers)\n\n if (!combinedDrillBounds) {\n console.log('[钻孔对齐] 无法计算钻孔层 bounds,跳过')\n return\n }\n\n // ==================== 2d 对齐逻辑的关键差异:钻孔坐标“倍数错误”兜底(只缩放孔位,不缩放孔径)====================\n // 某些 Excellon(.TXT/.DRL) 会在头部写 INCH,TZ,但实际坐标更像 LZ(或整体小数点错位),导致 X/Y 解析出来扩大 10/100 倍。\n // 2d 版本里有 checkAndScaleDrillLayers:如果钻孔 bounds 比参考层大很多就除以 10(孔径不变)。\n // 2d-direct 这里做等价修正,但更安全:\n // - 用参考层 bounds 计算倍率\n // - 仅当倍率接近 10^n 且修正后接近 1x 时才应用\n // - 仅移动每个孔的“中心”,不缩放孔径/半径(通过 drillPosScaleRuns 记录的顶点区间实现)\n // 用 displayName 作为“逻辑层类型”(支持 .art 的 Top/Bot/SST/SMT/... 映射)\n const getLayerType = (name) => getLayerDisplayName(name)\n const sizeOfBounds = (b) => Math.max(b.maxX - b.minX, b.maxY - b.minY)\n\n // ⚠️ 重要兜底:若 GKO/Outline 尺寸远大于铜层(例如 >5x),通常意味着轮廓文件坐标/单位异常\n // 或者是面板边框/工装边,直接拿它做对齐会把钻孔“拉偏”且让视图被撑大。\n // 这种情况下:对齐参考优先信任 GTL/GBL/GTS/GBS,而不是 Outline。\n if (outlineBounds) {\n const priority = ['GTL', 'GBL', 'GTS', 'GBS']\n let ref = null\n for (const t of priority) {\n const l = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile && getLayerType(x.name) === t)\n if (l) { ref = { name: l.name, bounds: l.bounds, reason: t }; break }\n }\n if (!ref) {\n const any = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile)\n if (any) ref = { name: any.name, bounds: any.bounds, reason: 'any' }\n }\n if (ref && ref.bounds) {\n const outlineSize = sizeOfBounds(outlineBounds)\n const refSize = sizeOfBounds(ref.bounds)\n const ratio = refSize > 0 ? (outlineSize / refSize) : Infinity\n if (Number.isFinite(ratio) && ratio > 5) {\n console.warn(`[钻孔对齐] ⚠️ Outline(${outlineLayer?.name || 'unknown'}) 尺寸=${outlineSize.toFixed(3)}mm 远大于参考层(${ref.reason}): ${ref.name} 尺寸=${refSize.toFixed(3)}mm (ratio=${ratio.toFixed(2)}),判定为“不可信Outline”,将跳过GKO兜底并用铜层做对齐参考。`)\n outlineBounds = null\n }\n }\n }\n const pickReferenceForScale = () => {\n if (outlineBounds) return { name: outlineLayer.name, bounds: outlineBounds, reason: 'outline' }\n const priority = ['GTL', 'GBL', 'GTS', 'GBS']\n for (const t of priority) {\n const l = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile && getLayerType(x.name) === t)\n if (l) return { name: l.name, bounds: l.bounds, reason: t }\n }\n const any = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile)\n if (any) return { name: any.name, bounds: any.bounds, reason: 'any' }\n return null\n }\n const decidePow10ScaleFix = (ratio) => {\n if (!Number.isFinite(ratio) || ratio <= 0) return 1\n const acceptLow = 0.7\n const acceptHigh = 1.3\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 >= acceptLow && newRatio <= acceptHigh) 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 >= acceptLow && newRatio <= acceptHigh) return cand\n }\n return 1\n }\n // 支持 X/Y 分别缩放(解决 web-gerber 对 DRL 的 X/Y 使用不同格式解析的问题)\n const applyPosScaleFixToDrills = (scaleX, scaleY, tag) => {\n const isUniform = (scaleX === scaleY)\n if (isUniform) {\n console.warn(`[钻孔对齐] ⚠️ 应用钻孔坐标缩放(${tag}): scale=${scaleX}(仅缩放孔位,不缩放孔径)`)\n } else {\n console.warn(`[钻孔对齐] ⚠️ 应用钻孔坐标缩放(${tag}): scaleX=${scaleX}, scaleY=${scaleY}(仅缩放孔位,不缩放孔径)`)\n }\n for (const dl of drillLayers) {\n if (!dl.rawVertices || !dl.drillPosScaleRuns || dl.drillPosScaleRuns.length === 0) continue\n const vertices = dl.rawVertices\n const runs = dl.drillPosScaleRuns\n\n for (const run of runs) {\n if (!run || !(run.count > 0) || run.start == null) continue\n const dx = (run.cx || 0) * (scaleX - 1)\n const dy = (run.cy || 0) * (scaleY - 1)\n const start = run.start * 3\n const end = (run.start + run.count) * 3\n for (let i = start; i < end; i += 3) {\n vertices[i] += dx\n vertices[i + 1] += dy\n }\n // 更新 run 的中心,避免后续误用\n run.cx = (run.cx || 0) * scaleX\n run.cy = (run.cy || 0) * scaleY\n }\n\n // 重新上传顶点\n gl.bindBuffer(gl.ARRAY_BUFFER, dl.vertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)\n\n // 重算 bounds\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n for (let k = 0; k < vertices.length; k += 3) {\n const x = vertices[k]\n const y = vertices[k + 1]\n minX = Math.min(minX, x)\n maxX = Math.max(maxX, x)\n minY = Math.min(minY, y)\n maxY = Math.max(maxY, y)\n }\n dl.bounds = { minX, minY, maxX, maxY }\n\n if (isUniform) {\n console.log(`[钻孔对齐] ✓ ${dl.name} 已应用坐标缩放 scale=${scaleX}`)\n } else {\n console.log(`[钻孔对齐] ✓ ${dl.name} 已应用坐标缩放 scaleX=${scaleX}, scaleY=${scaleY}`)\n }\n }\n }\n\n const refForScale = pickReferenceForScale()\n if (refForScale && refForScale.bounds) {\n const refBounds = refForScale.bounds\n const refWidth = refBounds.maxX - refBounds.minX\n const refHeight = refBounds.maxY - refBounds.minY\n const drillWidth = combinedDrillBounds.maxX - combinedDrillBounds.minX\n const drillHeight = combinedDrillBounds.maxY - combinedDrillBounds.minY\n\n // ===== 防误判:孔簇很小但本来就在板内时,不要触发“倍数缩放” =====\n // 典型案例:只有少量定位孔/边缘孔,drillW/drillH 远小于板子尺寸,但并不代表坐标缩放错误。\n const holeCount = drillLayers.reduce((n, dl) => n + ((dl && dl.drillPosScaleRuns) ? dl.drillPosScaleRuns.length : 0), 0)\n const overflowToRef = computeOverflow(combinedDrillBounds, refBounds)\n const refSize = Math.max(refWidth, refHeight)\n const inRef = overflowToRef.total <= 1e-3\n if (inRef) {\n console.log(`[钻孔对齐] ℹ️ Drill bounds 已在参考层(${refForScale.reason})范围内(holes=${holeCount}, drillW=${drillWidth.toFixed(3)}, drillH=${drillHeight.toFixed(3)}), 跳过 bounds-fit 坐标缩放兜底`)\n } else if (holeCount > 0 && holeCount < 50 && overflowToRef.total < refSize * 0.2) {\n // 小孔数且越界不明显:更倾向于后续“平移/特征匹配”,避免误缩放\n console.log(`[钻孔对齐] ℹ️ Drill 孔数较少(${holeCount})且越界不明显(total=${overflowToRef.total.toFixed(3)}mm),跳过 bounds-fit 坐标缩放兜底`)\n } else {\n \n // 分别计算 X 和 Y 的缩放因子\n const ratioX = drillWidth > 0 && refWidth > 0 ? drillWidth / refWidth : 1\n const ratioY = drillHeight > 0 && refHeight > 0 ? drillHeight / refHeight : 1\n const scaleX = decidePow10ScaleFix(ratioX)\n const scaleY = decidePow10ScaleFix(ratioY)\n \n // 防误判:只允许等比缩放(绝大多数单位/小数点错误都是统一倍率)\n if ((scaleX !== 1 || scaleY !== 1) && scaleX !== scaleY) {\n console.warn(`[钻孔对齐] ⚠️ bounds-fit 推断为非等比(scaleX=${scaleX}, scaleY=${scaleY}),为避免误缩放已忽略;将转而尝试平移/特征点对齐`)\n } else if (scaleX !== 1 || scaleY !== 1) {\n console.warn(`[钻孔对齐] ⚠️ Drill bounds 倍数异常:drillW=${drillWidth.toFixed(3)} drillH=${drillHeight.toFixed(3)} refW=${refWidth.toFixed(3)} refH=${refHeight.toFixed(3)} ratioX=${ratioX.toFixed(2)} ratioY=${ratioY.toFixed(2)} ref=${refForScale.name}(${refForScale.reason}) → 尝试 scaleX=${scaleX} scaleY=${scaleY}`)\n applyPosScaleFixToDrills(scaleX, scaleY, `bounds-fit (ref=${refForScale.reason})`)\n // 更新组合 bounds(用于后续 overflow / 对齐)\n combinedDrillBounds = combineBounds(drillLayers) || combinedDrillBounds\n }\n }\n } else {\n console.log('[钻孔对齐] 未找到参考层 bounds,跳过钻孔坐标缩放兜底')\n }\n \n let overflowBefore = null\n if (outlineBounds) {\n overflowBefore = computeOverflow(combinedDrillBounds, outlineBounds)\n console.log(`[钻孔对齐] GKO/Outline层: ${outlineLayer.name}`)\n console.log(`[钻孔对齐] Drill bounds(before): minX=${combinedDrillBounds.minX.toFixed(3)}, minY=${combinedDrillBounds.minY.toFixed(3)}, maxX=${combinedDrillBounds.maxX.toFixed(3)}, maxY=${combinedDrillBounds.maxY.toFixed(3)}`)\n console.log(`[钻孔对齐] Outline bounds: minX=${outlineBounds.minX.toFixed(3)}, minY=${outlineBounds.minY.toFixed(3)}, maxX=${outlineBounds.maxX.toFixed(3)}, maxY=${outlineBounds.maxY.toFixed(3)}`)\n console.log(`[钻孔对齐] 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)} (mm)`)\n } else {\n console.log('[钻孔对齐] 未找到GKO/Outline层,将仅使用特征点匹配对齐(若可用)')\n }\n\n // 统一应用偏移到所有钻孔层\n const applyOffsetToAllDrills = (dx, dy, tag) => {\n console.log(`[钻孔对齐] ⚠️ 应用全局偏移(${tag}): X=${dx.toFixed(3)}mm, Y=${dy.toFixed(3)}mm`)\n for (const dl of drillLayers) {\n if (!dl.rawVertices) continue\n\n const vertices = dl.rawVertices\n for (let k = 0; k < vertices.length; k += 3) {\n vertices[k] += dx\n vertices[k + 1] += dy\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, dl.vertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)\n\n if (dl.bounds) {\n dl.bounds.minX += dx\n dl.bounds.maxX += dx\n dl.bounds.minY += dy\n dl.bounds.maxY += dy\n }\n\n console.log(`[钻孔对齐] ✓ ${dl.name} 已应用全局偏移`)\n }\n\n if (outlineBounds) {\n const afterBounds = translateBounds(combinedDrillBounds, dx, dy)\n const overflowAfter = computeOverflow(afterBounds, outlineBounds)\n console.log(`[钻孔对齐] 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)} (mm)`)\n }\n\n console.log('[钻孔对齐] 对齐检查完成')\n }\n\n // 先尝试:特征点匹配对齐(加入“多参考层尝试 + 平移投票”),并保留 GKO sanity check 防止推到板外\n let featureOffset = null\n\n // 选取锚点钻孔层:\n // - 优先使用“圆心/中心点”(type==='circle') 作为几何特征(DRL 通孔、焊盘开窗等)\n // - 避免 SLOT 层(通常是路径点/轮廓点),否则会把大量“唯一点”当特征导致误选锚点\n // - 同等情况下优先非 VIA 文件\n let anchorLayer = null\n let anchorFeatures = null\n let anchorCircleCount = 0\n let anchorScore = -Infinity\n let fallbackLayer = null\n let fallbackFeatures = null\n let fallbackScore = -Infinity\n\n for (const dl of drillLayers) {\n if (!dl.rawVertices) continue\n const featsAll = extractFeaturesFromLayer(dl)\n if (featsAll.length < 3) continue\n\n const nameUpper = (dl.name || '').toUpperCase()\n const viaPenalty = /VIA/.test(nameUpper) ? 100000 : 0\n const slotPenalty = dl.isSlotFile ? 50000 : 0\n\n const circleFeats = featsAll.filter(f => f && f.type === 'circle')\n const circleCount = circleFeats.length\n\n // 优先:能提取到足够多的 circle 特征(典型 DRL)\n if (circleCount >= 3) {\n const score = circleCount * 1000 - viaPenalty - slotPenalty\n if (score > anchorScore) {\n anchorScore = score\n anchorLayer = dl\n anchorFeatures = circleFeats\n anchorCircleCount = circleCount\n }\n } else {\n // 兜底:没有 circle 特征时才用“唯一点”作为特征(例如某些特殊 drill/slot)\n const score = featsAll.length - viaPenalty - slotPenalty\n if (score > fallbackScore) {\n fallbackScore = score\n fallbackLayer = dl\n fallbackFeatures = featsAll\n }\n }\n }\n\n if (!anchorLayer && fallbackLayer) {\n anchorLayer = fallbackLayer\n anchorFeatures = fallbackFeatures\n anchorCircleCount = (anchorFeatures || []).filter(f => f && f.type === 'circle').length\n }\n\n if (!anchorLayer || !anchorFeatures) {\n console.log('[钻孔对齐] ⚠️ 未找到可用的锚点钻孔层(特征点不足),跳过特征点对齐')\n } else {\n console.log(`[钻孔对齐] 选定锚点钻孔层: ${anchorLayer.name} (features=${anchorFeatures.length}, circle=${anchorCircleCount}, slot=${!!anchorLayer.isSlotFile})`)\n\n // 多参考层候选:优先铜层,再阻焊层(因为阻焊常包含大量SMT开窗,不一定对应钻孔)\n const nonDrillLayers = layers.filter(layer => !layer.isDrillFile && !layer.isSlotFile)\n const getLayerType = (name) => getLayerDisplayName(name)\n const refTypePriority = ['GTL', 'GBL', 'GTS', 'GBS']\n const refCandidates = []\n for (const t of refTypePriority) {\n for (const l of nonDrillLayers) {\n if (!l || !l.rawVertices) continue\n if (getLayerType(l.name) === t) refCandidates.push(l)\n }\n }\n\n if (refCandidates.length === 0) {\n console.log('[钻孔对齐] 未找到可用参考层(GTL/GBL/GTS/GBS 或缺少 rawVertices),跳过特征点对齐')\n } else {\n const selectedAnchorFeatures = selectFeaturesWithXYDistribution(anchorFeatures, Math.min(80, anchorFeatures.length))\n let best = null\n\n let secondBest = null\n for (const refLayer of refCandidates) {\n console.log(`[钻孔对齐] 尝试参考层: ${refLayer.name}`)\n const refAll = extractFeaturesFromLayer(refLayer)\n const refCircle = refAll.filter(f => f && f.type === 'circle')\n const refFeatures = refCircle.length >= 3 ? refCircle : refAll\n if (refFeatures.length < 3) {\n console.log(`[钻孔对齐] 参考层特征点不足(${refFeatures.length}个),跳过`)\n continue\n }\n\n const selectedRefFeatures = selectFeaturesWithXYDistribution(refFeatures, Math.min(80, refFeatures.length))\n\n // 方案1:平移投票(更鲁棒)\n const vote = estimateOffsetByVoting(selectedAnchorFeatures, selectedRefFeatures, { binSize: 0.2, maxCount: 80, minVotes: 3 })\n if (!vote) {\n console.log('[钻孔对齐] 平移投票未得到稳定候选')\n } else {\n // 用“紧容差”评估质量,避免参考层过密导致“任何偏移都能满匹配”的歧义\n const tolLoose = 0.6\n const tolRefine = 0.3\n const tolTight = 0.2\n\n // 先用中等容差做一次匹配,用匹配对细化 dx/dy(均值)\n const vRef = verifyOffset(anchorFeatures, refFeatures, vote.dx, vote.dy, tolRefine)\n if (vRef.matchCount >= 2) {\n let sumDx = 0, sumDy = 0\n for (const p of vRef.matchedPairs) {\n sumDx += (p.refPoint.x - p.drillPoint.x)\n sumDy += (p.refPoint.y - p.drillPoint.y)\n }\n const refinedDx = sumDx / vRef.matchedPairs.length\n const refinedDy = sumDy / vRef.matchedPairs.length\n\n const vTight = verifyOffset(anchorFeatures, refFeatures, refinedDx, refinedDy, tolTight)\n const vLoose = verifyOffset(anchorFeatures, refFeatures, refinedDx, refinedDy, tolLoose)\n\n const cand = {\n dx: refinedDx,\n dy: refinedDy,\n matchTight: vTight.matchCount,\n maxErrorTight: vTight.maxError,\n avgErrorTight: vTight.avgError,\n matchLoose: vLoose.matchCount,\n maxErrorLoose: vLoose.maxError,\n avgErrorLoose: vLoose.avgError,\n refType: getLayerType(refLayer.name),\n refTypeRank: ({ GTL: 0, GBL: 1, GTS: 2, GBS: 3 }[getLayerType(refLayer.name)] ?? 9),\n refLayerName: refLayer.name,\n method: `vote(bin=${vote.binSize}, votes=${vote.votes})`\n }\n\n const isBetter = (a, b) => {\n if (!b) return true\n // 先比紧容差下的真实对齐数量,再比误差\n if (a.matchTight !== b.matchTight) return a.matchTight > b.matchTight\n if (a.avgErrorTight !== b.avgErrorTight) return a.avgErrorTight < b.avgErrorTight\n if (a.matchLoose !== b.matchLoose) return a.matchLoose > b.matchLoose\n if (a.avgErrorLoose !== b.avgErrorLoose) return a.avgErrorLoose < b.avgErrorLoose\n // 最后用“参考层类型优先级”打破平局(优先铜层,其次阻焊)\n if ((a.refTypeRank ?? 9) !== (b.refTypeRank ?? 9)) return (a.refTypeRank ?? 9) < (b.refTypeRank ?? 9)\n // 再用“偏移量更小”做兜底(避免把板子不必要地移开)\n const sa = Math.abs(a.dx) + Math.abs(a.dy)\n const sb = Math.abs(b.dx) + Math.abs(b.dy)\n if (sa !== sb) return sa < sb\n return false\n }\n\n if (isBetter(cand, best)) {\n secondBest = best\n best = cand\n } else if (isBetter(cand, secondBest)) {\n secondBest = cand\n }\n\n console.log(`[钻孔对齐] 投票候选: dx=${cand.dx.toFixed(3)} dy=${cand.dy.toFixed(3)} tight=${cand.matchTight} loose=${cand.matchLoose} errT(avg/max)=${(cand.avgErrorTight ?? 0).toFixed(3)}/${(cand.maxErrorTight ?? 0).toFixed(3)} (${cand.method})`)\n } else {\n console.log(`[钻孔对齐] 投票候选匹配不足(matchRef=${vRef.matchCount}),忽略`)\n }\n }\n }\n\n // 接受偏移的阈值:\n // - 绝对匹配数要够(避免偶然匹配)\n // - 最佳候选要明显优于第二名(避免歧义)\n const totalAnchor = anchorFeatures?.length || 0\n const minMatches = totalAnchor <= 10\n ? 3\n : totalAnchor <= 50\n ? 5\n : Math.max(8, Math.ceil(totalAnchor * 0.02)) // 大钻孔文件:至少 2% 或至少 8 个点\n\n const bestTight = best?.matchTight ?? 0\n const secondTight = secondBest?.matchTight ?? 0\n\n // 用 tight 匹配数做主要分离;若 tight 打平,则用 tight 误差分离;仍打平则视为歧义\n const separationByCount = secondTight === 0 ? true : (bestTight >= secondTight + 3 || bestTight >= Math.ceil(secondTight * 1.5))\n let separationByError = true\n if (!separationByCount && secondBest && bestTight === secondTight && secondTight > 0) {\n const be = best?.avgErrorTight ?? Infinity\n const se = secondBest?.avgErrorTight ?? Infinity\n separationByError = (be + 1e-6) < se * 0.8 || (se - be) > 0.05\n }\n let separationOk = separationByCount || separationByError\n // 若 tight/误差都打平,但两个候选的偏移几乎一致,则不应视为“歧义”\n if (!separationOk && best && secondBest && bestTight === secondTight && secondTight > 0) {\n const delta = Math.hypot((best.dx ?? 0) - (secondBest.dx ?? 0), (best.dy ?? 0) - (secondBest.dy ?? 0))\n if (delta < 0.05) {\n separationOk = true\n console.log(`[钻孔对齐] ℹ️ tight 打平但偏移几乎一致(Δ=${delta.toFixed(3)}mm),视为可靠`)\n }\n }\n\n if (best && bestTight >= minMatches && separationOk) {\n featureOffset = { dx: best.dx, dy: best.dy, matchCount: bestTight, maxError: best.maxErrorTight, avgError: best.avgErrorTight, refLayerName: best.refLayerName }\n console.log(`[钻孔对齐] ✓ 选择最佳参考层: ${best.refLayerName}`)\n console.log(`[钻孔对齐] ✓ 特征点候选偏移: X=${featureOffset.dx.toFixed(3)}mm, Y=${featureOffset.dy.toFixed(3)}mm (tight=${bestTight}/${totalAnchor}, secondTight=${secondTight}, errT(avg/max)=${(best.avgErrorTight ?? 0).toFixed(3)}/${(best.maxErrorTight ?? 0).toFixed(3)}mm, method=${best.method})`)\n } else {\n const bestLoose = best?.matchLoose ?? 0\n console.log(`[钻孔对齐] ⚠️ 多参考层尝试后未达到可靠匹配阈值:bestTight=${bestTight}/${totalAnchor} (min=${minMatches}), secondTight=${secondTight}, separationOk=${separationOk}, bestLoose=${bestLoose},跳过特征点对齐`)\n }\n }\n }\n\n // 决策:优先用“特征点对齐”,但必须通过 GKO 越界校验;否则 fallback 到“按 GKO bounds 自动回拉”\n const applyThresholdMm = 0.2\n let chosen = null\n\n if (featureOffset) {\n const dx = featureOffset.dx\n const dy = featureOffset.dy\n\n // 如果偏移量极小就不动\n if (Math.abs(dx) < applyThresholdMm && Math.abs(dy) < applyThresholdMm) {\n // 重要:特征点匹配显示 drill 已与参考层对齐,此时不应再触发 GKO 兜底(否则会把 drill 又挪歪)\n console.log(`[钻孔对齐] ✓ 特征点匹配显示钻孔已对齐(Δ<${applyThresholdMm}mm, match=${featureOffset.matchCount}, avgErr=${(featureOffset.avgError ?? 0).toFixed(3)}). 跳过对齐与GKO兜底`)\n return\n } else if (outlineBounds && overflowBefore) {\n const afterBounds = translateBounds(combinedDrillBounds, dx, dy)\n const overflowAfter = computeOverflow(afterBounds, outlineBounds)\n const improvement = overflowBefore.total - overflowAfter.total\n // 只要能显著减少越界(或至少不恶化),就认为是合理偏移\n if (improvement > 0.2 || overflowAfter.total <= overflowBefore.total + 0.1) {\n chosen = { dx, dy, tag: `feature-match (improve=${improvement.toFixed(3)}mm)` }\n } else {\n console.warn(`[钻孔对齐] ⚠️ 特征点偏移会让钻孔更越界(before=${overflowBefore.total.toFixed(3)} after=${overflowAfter.total.toFixed(3)}),拒绝并启用GKO兜底`)\n }\n } else {\n // 没有GKO可校验,退回到特征点结果\n chosen = { dx, dy, tag: 'feature-match (no-outline-check)' }\n }\n }\n\n if (!chosen && outlineBounds && overflowBefore) {\n // 如果已经明显越界,按 GKO bounds 计算最优平移(小集合候选,取越界最小者)\n if (overflowBefore.total <= 0.2) {\n console.log('[钻孔对齐] ✓ 钻孔层未明显越界,无需GKO兜底对齐')\n } else {\n const drillCenterX = (combinedDrillBounds.minX + combinedDrillBounds.maxX) / 2\n const drillCenterY = (combinedDrillBounds.minY + combinedDrillBounds.maxY) / 2\n const outlineCenterX = (outlineBounds.minX + outlineBounds.maxX) / 2\n const outlineCenterY = (outlineBounds.minY + outlineBounds.maxY) / 2\n\n const candidatesX = [\n 0,\n outlineBounds.minX - combinedDrillBounds.minX,\n outlineBounds.maxX - combinedDrillBounds.maxX,\n outlineCenterX - drillCenterX\n ]\n const candidatesY = [\n 0,\n outlineBounds.minY - combinedDrillBounds.minY,\n outlineBounds.maxY - combinedDrillBounds.maxY,\n outlineCenterY - 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(combinedDrillBounds, dx, dy)\n const ov = computeOverflow(afterBounds, outlineBounds)\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\n const improvement = overflowBefore.total - best.error\n console.log(`[钻孔对齐] GKO兜底候选偏移: X=${best.dx.toFixed(3)}mm, Y=${best.dy.toFixed(3)}mm (overflowAfter=${best.error.toFixed(3)}mm, improve=${improvement.toFixed(3)}mm)`)\n\n if ((Math.abs(best.dx) >= applyThresholdMm || Math.abs(best.dy) >= applyThresholdMm) && (improvement > 0.2 || best.error <= overflowBefore.total + 0.1)) {\n chosen = { dx: best.dx, dy: best.dy, tag: `outline-fit (improve=${improvement.toFixed(3)}mm)` }\n } else {\n console.log('[钻孔对齐] ⚠️ GKO兜底偏移不显著/过小,跳过')\n }\n }\n }\n\n // 若没有可用 Outline(或被判为不可信),尝试用“参考层 bounds”做一次简单回拉(防止完全不对齐)\n if (!chosen && !outlineBounds) {\n const ref = pickReferenceForScale()\n if (ref && ref.bounds) {\n const overflowRefBefore = computeOverflow(combinedDrillBounds, ref.bounds)\n if (overflowRefBefore.total > 0.2) {\n const drillCenterX = (combinedDrillBounds.minX + combinedDrillBounds.maxX) / 2\n const drillCenterY = (combinedDrillBounds.minY + combinedDrillBounds.maxY) / 2\n const refCenterX = (ref.bounds.minX + ref.bounds.maxX) / 2\n const refCenterY = (ref.bounds.minY + ref.bounds.maxY) / 2\n\n const candidatesX = [\n 0,\n ref.bounds.minX - combinedDrillBounds.minX,\n ref.bounds.maxX - combinedDrillBounds.maxX,\n refCenterX - drillCenterX\n ]\n const candidatesY = [\n 0,\n ref.bounds.minY - combinedDrillBounds.minY,\n ref.bounds.maxY - combinedDrillBounds.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(combinedDrillBounds, dx, dy)\n const ov = computeOverflow(afterBounds, ref.bounds)\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\n const improvement = overflowRefBefore.total - best.error\n if ((Math.abs(best.dx) >= applyThresholdMm || Math.abs(best.dy) >= applyThresholdMm) && (improvement > 0.2 || best.error <= overflowRefBefore.total + 0.1)) {\n console.warn(`[钻孔对齐] ⚠️ 无Outline时参考层回拉: ref=${ref.name}(${ref.reason}), dx=${best.dx.toFixed(3)} dy=${best.dy.toFixed(3)} improve=${improvement.toFixed(3)}mm`)\n chosen = { dx: best.dx, dy: best.dy, tag: `ref-fit (ref=${ref.reason}, improve=${improvement.toFixed(3)}mm)` }\n }\n }\n }\n }\n\n if (!chosen) {\n console.log('[钻孔对齐] 未选择任何有效偏移,跳过')\n return\n }\n\n applyOffsetToAllDrills(chosen.dx, chosen.dy, chosen.tag)\n}\n\n/**\n * 从图层的vertices数据中提取特征点(圆形孔的中心点)\n */\nfunction extractFeaturesFromLayer(layer) {\n if (!layer.rawVertices) return []\n \n const features = []\n const vertices = layer.rawVertices\n \n // 钻孔文件通常是圆形,每个圆由一个中心点+多个周边点组成\n // 我们通过检测顶点簇来识别圆心\n const centerCandidates = new Map() // key: \"x,y\", value: count\n \n for (let i = 0; i < vertices.length; i += 3) {\n const x = vertices[i]\n const y = vertices[i + 1]\n \n // 四舍五入到0.01mm精度,避免浮点误差\n const key = `${Math.round(x * 100) / 100},${Math.round(y * 100) / 100}`\n centerCandidates.set(key, (centerCandidates.get(key) || 0) + 1)\n }\n \n // 找出重复出现的点(这些可能是圆心)\n // 圆心应该重复至少10次(我们额外添加了10个中心点)\n const circleThreshold = 8 // 降低到8以应对可能的精度损失\n centerCandidates.forEach((count, key) => {\n if (count >= circleThreshold) {\n const [x, y] = key.split(',').map(Number)\n features.push({ x, y, type: 'circle', count: count })\n }\n })\n \n console.log(`[提取特征点] ${layer.name} - 找到 ${features.length} 个圆心候选点 (阈值=${circleThreshold})`)\n \n // 显示前5个圆心\n if (features.length > 0) {\n features.slice(0, 5).forEach((f, idx) => {\n console.log(` 圆心${idx + 1}: (${f.x.toFixed(3)}, ${f.y.toFixed(3)}), 重复次数=${f.count}`)\n })\n }\n \n // 如果没有找到重复点,尝试提取所有唯一点\n if (features.length === 0) {\n console.log(`[提取特征点] ${layer.name} - 未找到圆心,尝试提取唯一点`)\n const uniquePoints = new Set()\n for (let i = 0; i < vertices.length; i += 3) {\n const x = Math.round(vertices[i] * 100) / 100\n const y = Math.round(vertices[i + 1] * 100) / 100\n uniquePoints.add(`${x},${y}`)\n }\n \n uniquePoints.forEach(key => {\n const [x, y] = key.split(',').map(Number)\n features.push({ x, y, type: 'point' })\n })\n console.log(`[提取特征点] ${layer.name} - 提取到 ${features.length} 个唯一点`)\n }\n \n return features\n}\n\n/**\n * 选择特征点,优先选择四个角上的特征点(从2d版本移植)\n */\nfunction selectFeaturesWithXYDistribution(features, maxCount) {\n if (features.length <= maxCount) {\n return features\n }\n \n // 计算X和Y坐标的范围\n let minX = Infinity\n let maxX = -Infinity\n let minY = Infinity\n let maxY = -Infinity\n \n for (const feature of features) {\n minX = Math.min(minX, feature.x)\n maxX = Math.max(maxX, feature.x)\n minY = Math.min(minY, feature.y)\n maxY = Math.max(maxY, feature.y)\n }\n \n const rangeX = maxX - minX\n const rangeY = maxY - minY\n \n // 如果范围太小,使用简单的排序方法\n if (rangeX < 0.001 || rangeY < 0.001) {\n features.sort((a, b) => {\n if (Math.abs(a.y - b.y) > 0.0001) {\n return a.y - b.y\n }\n return a.x - b.x\n })\n return features.slice(0, maxCount)\n }\n \n const selectedFeatures = []\n const usedFeatures = new Set()\n \n // 定义四个角的区域(每个区域占整个范围的1/3)\n const cornerRegionSize = 0.33\n \n const corners = [\n { name: '左下角', xMin: minX, xMax: minX + rangeX * cornerRegionSize, yMin: minY, yMax: minY + rangeY * cornerRegionSize },\n { name: '右下角', xMin: maxX - rangeX * cornerRegionSize, xMax: maxX, yMin: minY, yMax: minY + rangeY * cornerRegionSize },\n { name: '左上角', xMin: minX, xMax: minX + rangeX * cornerRegionSize, yMin: maxY - rangeY * cornerRegionSize, yMax: maxY },\n { name: '右上角', xMin: maxX - rangeX * cornerRegionSize, xMax: maxX, yMin: maxY - rangeY * cornerRegionSize, yMax: maxY }\n ]\n \n // 第一步:从四个角各选择1个最接近角落的特征点\n for (const corner of corners) {\n const cornerFeatures = features.filter(f => \n f.x >= corner.xMin && f.x <= corner.xMax &&\n f.y >= corner.yMin && f.y <= corner.yMax &&\n !usedFeatures.has(f)\n )\n \n if (cornerFeatures.length > 0) {\n // 计算角落的极值点\n let cornerCenterX, cornerCenterY\n if (corner.name === '左下角') {\n cornerCenterX = minX\n cornerCenterY = minY\n } else if (corner.name === '右下角') {\n cornerCenterX = maxX\n cornerCenterY = minY\n } else if (corner.name === '左上角') {\n cornerCenterX = minX\n cornerCenterY = maxY\n } else {\n cornerCenterX = maxX\n cornerCenterY = maxY\n }\n \n // 优先选择圆形\n const circleFeatures = cornerFeatures.filter(f => f.type === 'circle')\n \n // 选择距离角落最近的特征点\n let closestFeature = null\n let minDistance = Infinity\n \n const featuresToCheck = circleFeatures.length > 0 ? circleFeatures : cornerFeatures\n for (const feature of featuresToCheck) {\n const distance = Math.sqrt(\n Math.pow(feature.x - cornerCenterX, 2) + Math.pow(feature.y - cornerCenterY, 2)\n )\n if (distance < minDistance) {\n minDistance = distance\n closestFeature = feature\n }\n }\n \n if (closestFeature) {\n selectedFeatures.push(closestFeature)\n usedFeatures.add(closestFeature)\n }\n }\n }\n \n console.log(`[特征点选择] 四个角共选择 ${selectedFeatures.length} 个特征点`)\n \n // 第二步:如果不足maxCount,从剩余区域补充\n if (selectedFeatures.length < maxCount) {\n const remainingFeatures = features.filter(f => !usedFeatures.has(f))\n \n if (remainingFeatures.length > 0) {\n // 使用网格方法从剩余区域选择\n const gridSize = Math.ceil(Math.sqrt(maxCount - selectedFeatures.length))\n const cellWidth = rangeX / gridSize\n const cellHeight = rangeY / gridSize\n \n const grid = Array(gridSize).fill(null).map(() => Array(gridSize).fill(null).map(() => []))\n \n for (const feature of remainingFeatures) {\n const gridX = Math.min(Math.floor((feature.x - minX) / cellWidth), gridSize - 1)\n const gridY = Math.min(Math.floor((feature.y - minY) / cellHeight), gridSize - 1)\n grid[gridY][gridX].push(feature)\n }\n \n for (let y = 0; y < gridSize; y++) {\n for (let x = 0; x < gridSize; x++) {\n const cellFeatures = grid[y][x]\n if (cellFeatures.length > 0) {\n const cellCenterX = minX + (x + 0.5) * cellWidth\n const cellCenterY = minY + (y + 0.5) * cellHeight\n \n let closestFeature = cellFeatures[0]\n let minDistance = Infinity\n \n for (const feature of cellFeatures) {\n const distance = Math.sqrt(\n Math.pow(feature.x - cellCenterX, 2) + Math.pow(feature.y - cellCenterY, 2)\n )\n if (distance < minDistance) {\n minDistance = distance\n closestFeature = feature\n }\n }\n \n selectedFeatures.push(closestFeature)\n usedFeatures.add(closestFeature)\n \n if (selectedFeatures.length >= maxCount) {\n break\n }\n }\n }\n if (selectedFeatures.length >= maxCount) {\n break\n }\n }\n \n console.log(`[特征点选择] 从剩余区域补充 ${selectedFeatures.length - (selectedFeatures.length > 4 ? 4 : selectedFeatures.length)} 个特征点`)\n }\n }\n \n console.log(`[特征点选择] 最终从 ${features.length} 个特征点中选择 ${selectedFeatures.length} 个`)\n \n return selectedFeatures\n}\n\n/**\n * 计算特征点之间的相对距离矩阵\n */\nfunction calculateRelativeDistances(features) {\n const distances = []\n \n for (let i = 0; i < features.length; i++) {\n for (let j = i + 1; j < features.length; j++) {\n const dx = features[j].x - features[i].x\n const dy = features[j].y - features[i].y\n const distance = Math.sqrt(dx * dx + dy * dy)\n \n distances.push({\n i,\n j,\n distance,\n dx,\n dy\n })\n }\n }\n \n // 按距离排序\n distances.sort((a, b) => a.distance - b.distance)\n \n return distances\n}\n\n/**\n * 基于相对距离匹配特征点(从2d版本移植)\n */\nfunction matchFeaturesByRelativeDistances(drillFeatures, refFeatures, drillDistances, refDistances) {\n const tolerance = 0.5 // 距离容差:0.5mm(从0.01英寸转换,约等于0.254mm,放宽到0.5mm)\n \n console.log(`[钻孔对齐] 开始匹配,钻孔层 ${drillFeatures.length} 个点,参考层 ${refFeatures.length} 个点`)\n \n let bestOverallMatch = null\n let bestMatchCount = 0\n \n // 尝试前10个最短距离\n const minDistancesToMatch = Math.min(10, drillDistances.length, refDistances.length)\n const drillShortestDistances = drillDistances.slice(0, minDistancesToMatch)\n \n for (const drillDist of drillShortestDistances) {\n // 在参考层中查找与钻孔层距离匹配的点对(容差范围内)\n for (const refDist of refDistances) {\n const distDiff = Math.abs(refDist.distance - drillDist.distance)\n \n if (distDiff <= tolerance) {\n // 找到匹配距离,计算两种可能的偏移量\n const drillPoint1 = drillFeatures[drillDist.i]\n const drillPoint2 = drillFeatures[drillDist.j]\n const refPoint1 = refFeatures[refDist.i]\n const refPoint2 = refFeatures[refDist.j]\n \n // 可能性1:drillPoint1 对应 refPoint1\n const offsetX1 = refPoint1.x - drillPoint1.x\n const offsetY1 = refPoint1.y - drillPoint1.y\n \n // 可能性2:drillPoint1 对应 refPoint2 \n const offsetX2 = refPoint2.x - drillPoint1.x\n const offsetY2 = refPoint2.y - drillPoint1.y\n \n // 验证哪种偏移量能匹配更多点\n const match1 = verifyOffset(drillFeatures, refFeatures, offsetX1, offsetY1, tolerance)\n const match2 = verifyOffset(drillFeatures, refFeatures, offsetX2, offsetY2, tolerance)\n \n const bestMatch = match1.matchCount >= match2.matchCount ? \n { offsetX: offsetX1, offsetY: offsetY1, ...match1 } :\n { offsetX: offsetX2, offsetY: offsetY2, ...match2 }\n \n // 更新最佳匹配\n if (bestMatch.matchCount > bestMatchCount) {\n bestMatchCount = bestMatch.matchCount\n bestOverallMatch = bestMatch\n console.log(`[钻孔对齐] 找到更好的匹配:${bestMatch.matchCount}/${drillFeatures.length} 个点,偏移量 (${bestMatch.offsetX.toFixed(3)}, ${bestMatch.offsetY.toFixed(3)})`)\n }\n \n // 如果匹配率 >= 80%,提前退出\n const matchRate = bestMatch.matchCount / drillFeatures.length\n if (matchRate >= 0.8) {\n console.log(`[钻孔对齐] ✓ 匹配率很高 (${(matchRate * 100).toFixed(1)}%)`)\n return { ...bestOverallMatch, success: true }\n }\n }\n }\n }\n \n // 如果找到任何匹配(≥2个点),返回最佳匹配\n if (bestOverallMatch && bestOverallMatch.matchCount >= 2) {\n const matchRate = bestOverallMatch.matchCount / drillFeatures.length\n console.log(`[钻孔对齐] ✓ 找到匹配:${bestOverallMatch.matchCount}/${drillFeatures.length} 个点 (${(matchRate * 100).toFixed(1)}%)`)\n return { ...bestOverallMatch, success: true }\n }\n \n console.log(`[钻孔对齐] ✗ 未找到足够的匹配点`)\n return { success: false }\n}\n\n/**\n * 验证给定偏移量能匹配多少个点\n */\nfunction verifyOffset(drillFeatures, refFeatures, offsetX, offsetY, tolerance) {\n let matchCount = 0\n let maxError = 0\n let sumError = 0\n const matchedPairs = []\n const usedRefIndices = new Set()\n \n // 对每个钻孔层点,应用偏移后查找最近的未使用的参考层点\n for (const drillPoint of drillFeatures) {\n const transformedX = drillPoint.x + offsetX\n const transformedY = drillPoint.y + offsetY\n \n // 查找最近的未使用的参考点\n let minDistance = Infinity\n let nearestRefPoint = null\n let nearestRefIndex = -1\n \n for (let i = 0; i < refFeatures.length; i++) {\n if (usedRefIndices.has(i)) continue\n \n const refPoint = refFeatures[i]\n const distance = Math.sqrt(\n Math.pow(transformedX - refPoint.x, 2) +\n Math.pow(transformedY - refPoint.y, 2)\n )\n \n if (distance < minDistance) {\n minDistance = distance\n nearestRefPoint = refPoint\n nearestRefIndex = i\n }\n }\n \n // 如果距离在容差范围内,认为匹配\n if (minDistance <= tolerance && nearestRefIndex !== -1) {\n matchCount++\n maxError = Math.max(maxError, minDistance)\n sumError += minDistance\n usedRefIndices.add(nearestRefIndex)\n matchedPairs.push({\n drillPoint: drillPoint,\n refPoint: nearestRefPoint,\n error: minDistance\n })\n }\n }\n \n const avgError = matchCount > 0 ? (sumError / matchCount) : Infinity\n return { matchCount, maxError, avgError, matchedPairs }\n}\n\n/**\n * 基于“平移投票”的偏移量估计:\n * 从 drill/ref 特征点子集中枚举(dx,dy)=ref-drill,按网格量化统计最高票的平移。\n * 这种方法对“参考层包含大量非钻孔圆焊盘(SMT)”更鲁棒(正确平移会形成明显的票数簇)。\n */\nfunction estimateOffsetByVoting(drillFeatures, refFeatures, options = {}) {\n const binSize = options.binSize ?? 0.2 // mm:量化网格\n const maxCount = options.maxCount ?? 80 // 参与投票的点数上限(防止 O(n^2) 爆炸)\n const minVotes = options.minVotes ?? 3\n\n if (!Array.isArray(drillFeatures) || !Array.isArray(refFeatures)) return null\n if (drillFeatures.length === 0 || refFeatures.length === 0) return null\n\n const drillSample = selectFeaturesWithXYDistribution(drillFeatures, Math.min(maxCount, drillFeatures.length))\n const refSample = selectFeaturesWithXYDistribution(refFeatures, Math.min(maxCount, refFeatures.length))\n\n const bins = new Map() // key => {count,sumDx,sumDy}\n for (const d of drillSample) {\n for (const r of refSample) {\n const dx = r.x - d.x\n const dy = r.y - d.y\n const qx = Math.round(dx / binSize)\n const qy = Math.round(dy / binSize)\n const key = `${qx},${qy}`\n const cur = bins.get(key) || { count: 0, sumDx: 0, sumDy: 0 }\n cur.count += 1\n cur.sumDx += dx\n cur.sumDy += dy\n bins.set(key, cur)\n }\n }\n\n let bestKey = null\n let best = null\n for (const [k, v] of bins.entries()) {\n if (!best || v.count > best.count) {\n best = v\n bestKey = k\n }\n }\n\n if (!best || best.count < minVotes) return null\n const dx = best.sumDx / best.count\n const dy = best.sumDy / best.count\n return { dx, dy, votes: best.count, binSize, key: bestKey }\n}\n\n/**\n * 查找参考层(优先级:GTS → GBS → GTL → GBL)\n */\nfunction findReferenceLayer() {\n // 提取图层类型\n const getLayerType = (name) => {\n const fileNameOnly = name.split(/[/\\\\]/).pop()\n const ext = '.' + fileNameOnly.split('.').pop().toUpperCase()\n return ext.substring(1)\n }\n \n // 只使用标准Gerber层作为参考(排除钻孔层)\n const nonDrillLayers = layers.filter(layer => !layer.isDrillFile && !layer.isSlotFile)\n \n if (nonDrillLayers.length === 0) {\n console.log('[钻孔对齐] 未找到非钻孔层')\n return null\n }\n \n // 按优先级查找:GTS → GBS → GTL → GBL\n const priorities = ['GTS', 'GBS', 'GTL', 'GBL']\n \n for (const type of priorities) {\n const layer = nonDrillLayers.find(l => getLayerType(l.name) === type && l.rawVertices)\n if (layer) {\n console.log(`[钻孔对齐] 使用参考层: ${layer.name} (${type})`)\n return layer\n }\n }\n \n console.log('[钻孔对齐] 未找到合适的参考层(GTS/GBS/GTL/GBL)')\n return null\n}\n\nfunction centerView() {\n // 优先使用GKO或GTO层的bounds来设置视图,避免偏移的钻孔层影响视图范围\n // 但如果GKO层很小,会同时考虑GTL层的边界,取最大值\n let viewBounds = null\n let useLargePadding = false // 标记是否使用大边距(40%)\n \n // 1. 查找GKO层和GTL层\n const gkoLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const display = getLayerDisplayName(fileName)\n return display === 'GKO' ||\n ext === 'GKO' || \n fileNameOnly.includes('_Profile') ||\n fileNameOnly.toLowerCase().includes('outline')\n })\n \n const gtlLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const display = getLayerDisplayName(fileName)\n return display === 'GTL' ||\n ext === 'GTL' || \n fileNameOnly.includes('_TOP') ||\n fileNameOnly.includes('_Copper_Signal_Top')\n })\n \n // 如果同时有GKO和GTL层,默认合并边界;但当 GKO 明显异常(尺寸远大于 GTL)时避免被撑大\n if (gkoLayer && gkoLayer.bounds && gtlLayer && gtlLayer.bounds) {\n const gkoBounds = gkoLayer.bounds\n const gtlBounds = gtlLayer.bounds\n \n // 计算边界大小\n const gkoWidth = gkoBounds.maxX - gkoBounds.minX\n const gkoHeight = gkoBounds.maxY - gkoBounds.minY\n const gkoSize = Math.max(gkoWidth, gkoHeight)\n \n const gtlWidth = gtlBounds.maxX - gtlBounds.minX\n const gtlHeight = gtlBounds.maxY - gtlBounds.minY\n const gtlSize = Math.max(gtlWidth, gtlHeight)\n \n const ratio = gtlSize > 0 ? (gkoSize / gtlSize) : Infinity\n console.log(`[视图居中] GKO层大小: ${gkoSize.toFixed(6)}, GTL层大小: ${gtlSize.toFixed(6)}`)\n\n if (Number.isFinite(ratio) && ratio > 5) {\n // GKO 明显异常:优先以 GTL 为视图基准(避免视图被撑大)\n viewBounds = { ...gtlBounds }\n console.warn(`[视图居中] ⚠️ GKO尺寸远大于GTL (ratio=${ratio.toFixed(2)} > 5),为避免视图被异常撑大,改为使用GTL层bounds设置视图`)\n console.log(`[视图居中] GTL: minX=${gtlBounds.minX.toFixed(6)}, minY=${gtlBounds.minY.toFixed(6)}, maxX=${gtlBounds.maxX.toFixed(6)}, maxY=${gtlBounds.maxY.toFixed(6)}`)\n } else {\n // 正常情况:合并边界(确保包含轮廓+铜层)\n viewBounds = {\n minX: Math.min(gkoBounds.minX, gtlBounds.minX),\n minY: Math.min(gkoBounds.minY, gtlBounds.minY),\n maxX: Math.max(gkoBounds.maxX, gtlBounds.maxX),\n maxY: Math.max(gkoBounds.maxY, gtlBounds.maxY)\n }\n console.log(`[视图居中] 使用GKO和GTL层的合并边界设置视图`)\n console.log(`[视图居中] GKO: minX=${gkoBounds.minX.toFixed(6)}, minY=${gkoBounds.minY.toFixed(6)}, maxX=${gkoBounds.maxX.toFixed(6)}, maxY=${gkoBounds.maxY.toFixed(6)}`)\n console.log(`[视图居中] GTL: minX=${gtlBounds.minX.toFixed(6)}, minY=${gtlBounds.minY.toFixed(6)}, maxX=${gtlBounds.maxX.toFixed(6)}, maxY=${gtlBounds.maxY.toFixed(6)}`)\n console.log(`[视图居中] 合并: minX=${viewBounds.minX.toFixed(6)}, minY=${viewBounds.minY.toFixed(6)}, maxX=${viewBounds.maxX.toFixed(6)}, maxY=${viewBounds.maxY.toFixed(6)}`)\n }\n } else if (gkoLayer && gkoLayer.bounds) {\n // 只有GKO层\n viewBounds = { ...gkoLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用GKO层 (${gkoLayer.name}) 的bounds设置视图`)\n } else if (gtlLayer && gtlLayer.bounds) {\n // 只有GTL层\n viewBounds = { ...gtlLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用GTL层 (${gtlLayer.name}) 的bounds设置视图`)\n } else {\n // 3. 如果没有GKO和GTL层,查找GTO层\n const gtoLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const display = getLayerDisplayName(fileName)\n return display === 'GTO' ||\n ext === 'GTO' || \n fileNameOnly.includes('_Legend_Top') ||\n (fileNameOnly.toLowerCase().includes('legend') && fileNameOnly.toLowerCase().includes('top')) ||\n (fileNameOnly.toLowerCase().includes('silkscreen') && fileNameOnly.toLowerCase().includes('top'))\n })\n \n if (gtoLayer && gtoLayer.bounds) {\n viewBounds = { ...gtoLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用GTO层 (${gtoLayer.name}) 的bounds设置视图`)\n } else {\n // 4. 如果没有GKO/GTL/GTO层,查找以art开头的文件作为轮廓层\n const artLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n return fileNameOnly.toLowerCase().startsWith('art')\n })\n \n if (artLayer && artLayer.bounds) {\n viewBounds = { ...artLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用art开头文件 (${artLayer.name}) 的bounds设置视图`)\n } else {\n // 5. 如果都没有,使用所有图层的边界(使用大边距)\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n layers.forEach(layer => {\n if (layer.bounds) {\n minX = Math.min(minX, layer.bounds.minX)\n maxX = Math.max(maxX, layer.bounds.maxX)\n minY = Math.min(minY, layer.bounds.minY)\n maxY = Math.max(maxY, layer.bounds.maxY)\n }\n })\n viewBounds = { minX, maxX, minY, maxY }\n useLargePadding = true\n console.log(`[视图居中] 未找到GKO/GTL/GTO/art开头文件,使用所有图层边界(大边距)`)\n }\n }\n }\n \n if (!viewBounds) return\n \n const width = viewBounds.maxX - viewBounds.minX\n const height = viewBounds.maxY - viewBounds.minY\n \n if (width <= 0 || height <= 0) {\n console.warn(`[视图居中] 无效边界: width=${width}, height=${height}`)\n return\n }\n \n // 根据是否使用大边距决定padding大小\n // 与 2d 页面保持一致:如果useLargePadding为true,使用40%边距;否则使用10%边距\n const paddingRatio = useLargePadding ? 0.40 : 0.1\n const padding = Math.max(width, height) * paddingRatio\n const paddedWidth = width + padding * 2\n const paddedHeight = height + padding * 2\n \n const cx = (viewBounds.minX + viewBounds.maxX) / 2\n const cy = (viewBounds.minY + viewBounds.maxY) / 2\n \n const aspect = canvas.width / canvas.height\n const contentAspect = paddedWidth / paddedHeight\n \n // 计算缩放比例,使内容适应画布\n // 使用 1.0 倍,让内容显示稍大一些(比 2d 页面的 0.9 倍稍大)\n if (contentAspect > aspect) {\n // 内容更宽,以宽度为准\n scale = 2.0 / paddedWidth * 1.4\n } else {\n // 内容更高,以高度为准\n scale = (2.0 / paddedHeight) / aspect * 1.4\n }\n \n transX = -cx * (scale / aspect)\n transY = -cy * scale\n \n console.log(`[视图居中] 边界: [${viewBounds.minX.toFixed(2)}, ${viewBounds.maxX.toFixed(2)}] x [${viewBounds.minY.toFixed(2)}, ${viewBounds.maxY.toFixed(2)}]`)\n console.log(`[视图居中] 缩放: ${scale.toFixed(6)}, 平移: (${transX.toFixed(6)}, ${transY.toFixed(6)})`)\n}\n\n// --- 初始化 URL ID 处理 ---\n// 创建一个包装函数来处理文件选择(适配 2d-direct 的文件处理逻辑)\nasync function handleFileSelectionForDirect(files) {\n if (files.length === 0) return\n \n const file = files[0]\n if (!file) return\n \n // 触发 fileInput 的 change 事件,使用 2d-direct 现有的文件处理逻辑\n // 创建一个新的 FileList 对象\n const dataTransfer = new DataTransfer()\n dataTransfer.items.add(file)\n fileInput.files = dataTransfer.files\n \n // 触发 change 事件\n const event = new Event('change', { bubbles: true })\n fileInput.dispatchEvent(event)\n}\n\n// 页面加载完成后初始化\nwindow.addEventListener('load', () => {\n // 初始化 URL ID 处理(如果 URL 中有 id,自动获取并处理文件)\n initUrlIdHandler(handleFileSelectionForDirect, showStatus)\n\n // 嵌入 @yumiai/chat-widget 时无独立 Vite BASE_URL;避免 tsup CJS 对 import.meta 告警\n const baseUrl = '/'\n const buildPageUrl = (path) => `${baseUrl}${path.replace(/^\\/+/, '')}`\n const getTargetUrl = (basePath) => {\n const targetBase = buildPageUrl(basePath)\n const id = parseUrlId()\n if (id) return `${targetBase}?id=${encodeURIComponent(id)}`\n const url = parseUrlFileUrl()\n if (url) return `${targetBase}?url=${encodeURIComponent(url)}`\n return targetBase\n }\n \n // 绑定切换模式按钮(跳转回标准模式页面)\n // const toggleModeBtn = document.querySelector('.viewer-action-btn:nth-child(1)')\n // if (toggleModeBtn) {\n // toggleModeBtn.addEventListener('click', () => {\n // // 从当前 URL 中提取 ID(支持路径格式和查询参数格式)\n // const id = parseUrlId()\n \n // if (id) {\n // // 使用新的路径格式:/2d/{id}\n // const newUrl = new URL(`../2d/${id}`, window.location.origin)\n // window.location.href = newUrl.toString()\n // } else {\n // // 如果没有 ID,跳转到默认页面\n // window.location.href = '../2d/index.html'\n // }\n // })\n // }\n \n // 绑定 3D 模式按钮\n const mode3DBtn = document.querySelector('#mode3DBtnId')\n if (mode3DBtn) {\n mode3DBtn.addEventListener('click', () => {\n window.location.href = getTargetUrl('/3dPage')\n })\n }\n\n // 绑定仿真图按钮\n const modeSimulationBtn = document.querySelector('#modeSimulationBtnId')\n if (modeSimulationBtn) {\n modeSimulationBtn.addEventListener('click', () => {\n window.location.href = getTargetUrl('/simulation')\n })\n }\n})\n\n"],"mappings":"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","names":["scale","earcut","gl","fileName","parseSvgPath","scale","earcut","centerIdx","layers","JSZip","getLayerType","dx","dy","fileName"]}
1
+ {"version":3,"sources":["../src/components/jetPaveGerberViewer/src/viewer-src/2dPage/js/gerber-parser-direct.js","../src/components/jetPaveGerberViewer/src/viewer-src/2dPage/js/main.js"],"sourcesContent":["/**\n * 直接解析 Gerber 文件并转换为 WebGL 几何数据\n * 不依赖 web-gerber,直接处理 G-code\n */\nimport earcut from 'earcut'\n\nexport class GerberDirectParser {\n constructor(options = {}) {\n this.vertices = [] // x, y, z\n this.colors = [] // r, g, b\n this.indices = [] // for line outlines (optional)\n this.triangleIndices = [] // for solid fills\n this.features = [] // 保存特征点(圆形、矩形等),用于钻孔对齐\n \n // 配置选项\n this.minLineWidth = options.minLineWidth || 0\n\n // 状态机\n this.state = {\n x: 0,\n y: 0,\n i: 0,\n j: 0,\n d: 0, // D-code (1: draw, 2: move, 3: flash)\n g: 1, // G-code (1: linear, 2: cw arc, 3: ccw arc, 36/37: region)\n aperture: null, // 当前光圈 D-code (例如 10, 11...)\n interpolation: 'linear', // linear, cw, ccw\n regionMode: false, // G36/G37\n quadrant: 'multi', // 'single' or 'multi' (G74/G75)\n unit: 'mm', // 'mm' or 'inch'\n polarity: 'dark', // 'dark' (正极性,填充) 或 'clear' (负极性,挖空)\n format: {\n integer: 2,\n decimal: 4,\n zero: 'L' // 'L'eading or 'T'railing\n }\n }\n\n // 极性段落(按 Gerber 命令顺序)。用于渲染阶段做 stencil:dark 写 1,clear 写 0。\n // 记录的是 triangleIndices 的区间 [start, end),所以不需要额外拆分 buffer。\n this.polarityRuns = [{ polarity: this.state.polarity, start: 0, end: 0 }]\n\n // 光圈定义字典\n this.apertures = {}\n // 宏定义字典\n this.macros = {}\n \n // 区域路径缓存(Gerber Region: G36/G37)\n // 一个 Region 里可能包含多个轮廓(D02 分段),并且存在孔洞。\n this.currentRegionContours = [] // Array<Array<{x:number,y:number}>>\n this._currentRegionContour = null\n }\n\n parse(content) {\n const lines = content.split(/[\\n\\r]+/)\n \n for (let line of lines) {\n line = line.trim()\n if (!line) continue\n \n // 处理扩展命令 (%)\n if (line.startsWith('%') && line.endsWith('%')) {\n this.parseExtendedCommand(line.substring(1, line.length - 1))\n continue\n }\n \n // 处理基本命令 (G, D, M, X, Y)\n // 基本命令可能在一行中有多个,以 * 结尾\n const commands = line.split('*')\n for (let cmd of commands) {\n cmd = cmd.trim()\n if (!cmd) continue\n this.parseCommand(cmd)\n }\n }\n\n // 结束最后一个 polarity run\n if (this.polarityRuns.length > 0) {\n this.polarityRuns[this.polarityRuns.length - 1].end = this.triangleIndices.length\n }\n\n return {\n vertices: new Float32Array(this.vertices),\n colors: new Float32Array(this.colors),\n triangleIndices: new Uint32Array(this.triangleIndices), // 改用 Uint32Array 支持大索引\n features: this.features, // 返回特征点\n // 仅返回非空 run,避免产生大量空段\n polarityRuns: this.polarityRuns.filter(r => (r.end - r.start) > 0)\n }\n }\n\n parseExtendedCommand(cmd) {\n // 格式定义 FS\n if (cmd.startsWith('FS')) {\n // FSLAX24Y24\n const match = cmd.match(/FS([LT])?[AI]X(\\d)(\\d)Y(\\d)(\\d)/)\n if (match) {\n this.state.format.zero = match[1] || 'L'\n this.state.format.integer = parseInt(match[2])\n this.state.format.decimal = parseInt(match[3])\n }\n }\n // 单位定义 MO\n else if (cmd.startsWith('MO')) {\n this.state.unit = cmd.includes('IN') ? 'inch' : 'mm'\n }\n // 光圈定义 AD\n else if (cmd.startsWith('AD')) {\n // ADD10C,0.0100\n const match = cmd.match(/ADD(\\d+)([a-zA-Z]+),?(.*)/)\n if (match) {\n const dCode = parseInt(match[1])\n const type = match[2]\n const params = match[3].split('X').map(p => parseFloat(p))\n this.apertures[dCode] = { type, params }\n }\n }\n // 极性 LP(LPD: dark, LPC: clear)\n else if (cmd.startsWith('LP')) {\n if (cmd.startsWith('LPD')) this._switchPolarity('dark')\n else if (cmd.startsWith('LPC')) this._switchPolarity('clear')\n }\n // 宏定义 AM (暂略,复杂)\n // 注意:本项目当前主要依赖“Region 的多轮廓/孔洞”表达镂空(本文件无 LP)。\n // 若后续需要完整支持 LP(dark/clear)做布尔差集,应在渲染阶段按 polarity 做 stencil/布尔运算。\n }\n\n _switchPolarity(nextPolarity) {\n if (!nextPolarity || this.state.polarity === nextPolarity) return\n\n // 结束当前 run\n if (this.polarityRuns.length > 0) {\n this.polarityRuns[this.polarityRuns.length - 1].end = this.triangleIndices.length\n }\n\n this.state.polarity = nextPolarity\n\n // 开始新 run\n const start = this.triangleIndices.length\n this.polarityRuns.push({ polarity: nextPolarity, start, end: start })\n }\n\n parseCommand(cmd) {\n // G 命令\n if (cmd.startsWith('G')) {\n const gCode = parseInt(cmd.substring(1, 3))\n // G54:选择光圈(Aperture Select)。很多Gerber会用 G54D10* / G54D11* 切换光圈。\n // 之前我们把它当作普通G命令并走 parseCoordinate,导致 D10 被误当成“操作码”而不是“选择光圈”,\n // 从而 state.aperture 一直为 null,所有线段/焊盘都会被跳过(只剩下G36/G37区域能显示)。\n if (gCode === 54) {\n const dSel = cmd.match(/D(\\d+)/)\n if (dSel) {\n const dCode = parseInt(dSel[1], 10)\n if (dCode >= 10) {\n this.state.aperture = dCode\n return\n }\n }\n // 如果没有 D10+,则忽略\n return\n }\n if (gCode === 1) this.state.interpolation = 'linear'\n if (gCode === 2) this.state.interpolation = 'cw'\n if (gCode === 3) this.state.interpolation = 'ccw'\n if (gCode === 36) {\n this.state.regionMode = true\n this.currentRegionContours = []\n this._currentRegionContour = null\n }\n if (gCode === 37) {\n this.state.regionMode = false\n this.finishRegion()\n }\n \n // G命令后可能紧跟坐标\n if (cmd.length > 3) {\n this.parseCoordinate(cmd.substring(3))\n }\n return\n }\n\n // D 命令\n if (cmd.startsWith('D')) {\n const dCode = parseInt(cmd.substring(1))\n // D10+ 是选择光圈\n if (dCode >= 10) {\n this.state.aperture = dCode\n return\n }\n \n // D01/D02/D03 作为独立命令(没有坐标),使用当前状态位置执行操作\n if (dCode === 1 || dCode === 2 || dCode === 3) {\n // 使用当前状态的位置\n const x = this.state.x\n const y = this.state.y\n \n if (dCode === 1) { // Draw\n // 独立 D01,如果有光圈,绘制线段到当前点(相当于点)\n if (this.state.aperture) {\n // 这里通常应该结合上一个点画线,但如果原地不动,可能就是个点\n // 暂且当作 Flash 处理,或者忽略\n }\n } else if (dCode === 2) { // Move\n // 仅更新位置,无需操作\n } else if (dCode === 3) { // Flash\n this.addFlash(x, y)\n }\n return\n }\n }\n\n // 坐标操作 X...Y...D...\n this.parseCoordinate(cmd)\n }\n\n parseCoordinate(cmd) {\n let x = this.state.x\n let y = this.state.y\n let iOffset = 0\n let jOffset = 0\n let d = null\n\n // 解析 X\n const xMatch = cmd.match(/X([-+]?\\d+)/)\n if (xMatch) {\n x = this.parseValue(xMatch[1])\n }\n\n // 解析 Y\n const yMatch = cmd.match(/Y([-+]?\\d+)/)\n if (yMatch) {\n y = this.parseValue(yMatch[1])\n }\n \n // 解析 I (圆心偏移 X)\n const iMatch = cmd.match(/I([-+]?\\d+)/)\n if (iMatch) {\n iOffset = this.parseValue(iMatch[1])\n }\n\n // 解析 J (圆心偏移 Y)\n const jMatch = cmd.match(/J([-+]?\\d+)/)\n if (jMatch) {\n jOffset = this.parseValue(jMatch[1])\n }\n\n // 解析 D (1, 2, 3)\n const dMatch = cmd.match(/D(\\d+)/)\n if (dMatch) {\n d = parseInt(dMatch[1])\n }\n\n // 更新状态 (X, Y)\n const prevX = this.state.x\n const prevY = this.state.y\n this.state.x = x\n this.state.y = y\n\n // 执行操作\n if (d !== null) {\n if (d === 1) { // Draw / Expose\n if (this.state.regionMode) {\n // 区域模式\n if (this.state.interpolation === 'linear') {\n // 如果 region 里没有显式 D02 起笔,则使用“上一点”作为起点创建轮廓\n if (!this._currentRegionContour) {\n this._startRegionContour(prevX, prevY)\n }\n this._currentRegionContour.push({ x, y })\n } else {\n // 区域模式下的圆弧:需要离散化为点加入路径\n if (!this._currentRegionContour) {\n this._startRegionContour(prevX, prevY)\n }\n this.addArcToRegion(prevX, prevY, x, y, iOffset, jOffset, this.state.interpolation === 'cw')\n }\n } else {\n // 线性模式\n if (this.state.interpolation === 'linear') {\n this.addStroke(prevX, prevY, x, y)\n // Round Caps\n if (this.state.aperture && this.apertures[this.state.aperture]?.type === 'C') {\n let width = this.apertures[this.state.aperture].params[0]\n if (this.state.unit === 'inch') width *= 25.4\n if (width < this.minLineWidth) width = this.minLineWidth\n this.addCircle(prevX, prevY, width / 2)\n this.addCircle(x, y, width / 2)\n }\n } else {\n // 圆弧插值\n this.addArcStroke(prevX, prevY, x, y, iOffset, jOffset, this.state.interpolation === 'cw')\n }\n }\n } else if (d === 2) { // Move\n if (this.state.regionMode) {\n // Region 内 D02:开始一个新轮廓(用于孔洞/多多边形)\n this._startRegionContour(x, y)\n }\n // 仅更新位置\n } else if (d === 3) { // Flash\n this.addFlash(x, y)\n }\n }\n }\n\n _startRegionContour(x, y) {\n // 结束旧轮廓:不需要显式 close(earcut 不要求重复首点),后面会做去重/清洗\n const contour = [{ x, y }]\n this.currentRegionContours.push(contour)\n this._currentRegionContour = contour\n }\n \n // 添加圆弧绘制逻辑\n addArcStroke(x1, y1, x2, y2, iOffset, jOffset, isCw) {\n const cx = x1 + iOffset\n const cy = y1 + jOffset\n const radius = Math.sqrt(iOffset * iOffset + jOffset * jOffset)\n \n // [调试日志]\n // console.log(`[addArcStroke] Start:(${x1.toFixed(2)}, ${y1.toFixed(2)}) End:(${x2.toFixed(2)}, ${y2.toFixed(2)}) I=${iOffset.toFixed(2)} J=${jOffset.toFixed(2)} R=${radius.toFixed(2)} CW=${isCw}`)\n\n // 如果半径太小,忽略\n if (radius < 0.0001) {\n console.warn('Radius too small, skipping arc')\n return\n }\n \n // console.log(`[Arc] R=${radius.toFixed(3)} (${x1.toFixed(3)},${y1.toFixed(3)})->(${x2.toFixed(3)},${y2.toFixed(3)}) CW=${isCw}`)\n\n let startAngle = Math.atan2(y1 - cy, x1 - cx)\n let endAngle = Math.atan2(y2 - cy, x2 - cx)\n \n // 检测全圆 (Start ~= End)\n const dist = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))\n const isFullCircle = dist < 0.0001\n \n if (isCw) { // 顺时针\n if (isFullCircle) {\n endAngle = startAngle - 2 * Math.PI\n } else if (endAngle >= startAngle) {\n endAngle -= 2 * Math.PI\n }\n } else { // 逆时针\n if (isFullCircle) {\n endAngle = startAngle + 2 * Math.PI\n } else if (endAngle <= startAngle) {\n endAngle += 2 * Math.PI\n }\n }\n \n // 离散化\n // 估算弧长\n const arcLength = Math.abs(endAngle - startAngle) * radius\n // 根据弧长决定分段,精度控制在 0.1mm(降低精度提升性能)\n const segments = Math.ceil(arcLength / 0.1) \n const limitedSegments = Math.min(Math.max(segments, 8), 64) // 降低分段数:最小 8,最大 64\n \n // console.log(`[addArcStroke] Angles: ${startAngle.toFixed(2)} -> ${endAngle.toFixed(2)}, Segments: ${limitedSegments}`)\n\n let prevPx = x1\n let prevPy = y1\n \n // 只在圆弧起点加圆(端点会在循环结束后单独加)\n if (this.state.aperture && this.apertures[this.state.aperture]?.type === 'C') {\n let width = this.apertures[this.state.aperture].params[0]\n if (this.state.unit === 'inch') width *= 25.4\n if (width < this.minLineWidth) width = this.minLineWidth\n this.addCircle(x1, y1, width / 2)\n }\n \n for (let k = 1; k <= limitedSegments; k++) {\n const t = k / limitedSegments\n const angle = startAngle + (endAngle - startAngle) * t\n const px = cx + radius * Math.cos(angle)\n const py = cy + radius * Math.sin(angle)\n \n this.addStroke(prevPx, prevPy, px, py)\n \n // 不再在每个分段点加圆,避免过度遮挡\n \n prevPx = px\n prevPy = py\n }\n \n // 在圆弧终点加圆\n if (this.state.aperture && this.apertures[this.state.aperture]?.type === 'C') {\n let width = this.apertures[this.state.aperture].params[0]\n if (this.state.unit === 'inch') width *= 25.4\n if (width < this.minLineWidth) width = this.minLineWidth\n this.addCircle(x2, y2, width / 2)\n }\n }\n \n addArcToRegion(x1, y1, x2, y2, iOffset, jOffset, isCw) {\n const cx = x1 + iOffset\n const cy = y1 + jOffset\n const radius = Math.sqrt(iOffset * iOffset + jOffset * jOffset)\n \n if (radius < 0.0001) return\n\n let startAngle = Math.atan2(y1 - cy, x1 - cx)\n let endAngle = Math.atan2(y2 - cy, x2 - cx)\n \n const dist = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2))\n const isFullCircle = dist < 0.0001\n \n if (isCw) {\n if (isFullCircle) {\n endAngle = startAngle - 2 * Math.PI\n } else if (endAngle >= startAngle) {\n endAngle -= 2 * Math.PI\n }\n } else {\n if (isFullCircle) {\n endAngle = startAngle + 2 * Math.PI\n } else if (endAngle <= startAngle) {\n endAngle += 2 * Math.PI\n }\n }\n \n const arcLength = Math.abs(endAngle - startAngle) * radius\n const segments = Math.max(8, Math.ceil(arcLength / 0.1)) // 降低精度以提升性能\n \n for (let k = 1; k <= segments; k++) {\n const t = k / segments\n const angle = startAngle + (endAngle - startAngle) * t\n if (this._currentRegionContour) {\n this._currentRegionContour.push({\n x: cx + radius * Math.cos(angle),\n y: cy + radius * Math.sin(angle),\n })\n }\n }\n }\n\n parseValue(str) {\n // 根据 FS 格式解析坐标值\n // 简单实现:假设 trailing zeros 省略 (常见的 Altium 输出)\n // 实际上应该根据 this.state.format.zero 和 integer/decimal 严谨解析\n \n // 假设是 mm, 小数点后4位 (常见配置)\n // 如果 str 包含小数点,直接解析\n if (str.includes('.')) return parseFloat(str)\n\n // 否则按定点数解析\n const totalLen = this.state.format.integer + this.state.format.decimal\n const sign = str.startsWith('-') || str.startsWith('+') ? str[0] : ''\n const numStr = sign ? str.substring(1) : str\n \n let val = 0\n // 这里简化处理,通用做法:\n // 如果是 Leading Zero Omission (省略前导零)\n // 需要补足位数,然后应用小数点\n \n // 让我们先用一个简单的 heuristic:通常 Gerber 单位是 mm 或 inch\n // 坐标值通常由 format 决定。\n // 比如 2:4 格式,'15000' -> 1.5000\n \n const scale = Math.pow(10, this.state.format.decimal)\n val = parseInt(str) / scale\n \n // 简单的单位转换到 WebGL 单位 (保持 mm 为基准)\n if (this.state.unit === 'inch') val *= 25.4\n \n return val\n }\n\n addStroke(x1, y1, x2, y2) {\n if (!this.state.aperture) {\n console.warn('[addStroke] No aperture selected')\n return\n }\n const ap = this.apertures[this.state.aperture]\n if (!ap) {\n console.warn(`[addStroke] Aperture D${this.state.aperture} not defined`)\n return\n }\n\n // 获取线宽\n let width = 0\n if (ap.type === 'C') width = ap.params[0] // Circle diameter\n if (ap.type === 'R') width = ap.params[0] // Rect width (approximation)\n \n if (this.state.unit === 'inch') width *= 25.4\n \n // 处理0宽度aperture(防止GG1等文件中的0宽度定义导致不渲染)\n if (width === 0) {\n console.warn(`[GerberDirect] Aperture D${this.state.aperture} 宽度为0,使用默认宽度 0.1mm`)\n width = 0.1\n }\n \n // 应用最小线宽\n if (width < this.minLineWidth) {\n width = this.minLineWidth\n }\n \n // 调试日志(仅对前几条线段)\n // if (!this._strokeCount) this._strokeCount = 0\n // if (this._strokeCount < 5) {\n // console.log(`[addStroke #${this._strokeCount}] (${x1.toFixed(2)},${y1.toFixed(2)})->(${x2.toFixed(2)},${y2.toFixed(2)}) Width=${width.toFixed(3)}mm Aperture=D${this.state.aperture}`)\n // }\n // this._strokeCount++\n\n // 三角化线段 (生成矩形)\n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx*dx + dy*dy)\n if (len === 0) return\n\n const ux = -dy / len * (width / 2)\n const uy = dx / len * (width / 2)\n\n // 四个角点\n const p1 = { x: x1 + ux, y: y1 + uy }\n const p2 = { x: x1 - ux, y: y1 - uy }\n const p3 = { x: x2 - ux, y: y2 - uy }\n const p4 = { x: x2 + ux, y: y2 + uy }\n\n this.addQuad(p1, p2, p3, p4)\n \n // 添加圆形接头(Round Cap)以填补连接处的缺口\n // 仅当光圈是圆形(Type C)时才添加圆形接头\n // 如果是矩形光圈绘制的线,通常应该是平头,但实际 Gerber 中很少用矩形光圈画线\n if (ap.type === 'C') {\n this.addCircle(x1, y1, width / 2)\n this.addCircle(x2, y2, width / 2)\n }\n }\n\n addFlash(x, y) {\n if (!this.state.aperture) return\n const ap = this.apertures[this.state.aperture]\n if (!ap) return\n\n if (ap.type === 'C') {\n // 圆形 Flash\n let r = ap.params[0] / 2\n if (this.state.unit === 'inch') r *= 25.4\n \n // 处理0半径aperture\n if (r === 0) {\n console.warn(`[GerberDirect] Aperture D${this.state.aperture} 半径为0,使用默认半径 0.05mm (直径0.1mm)`)\n r = 0.05\n }\n \n this.addCircle(x, y, r)\n \n // 记录特征点(圆形)\n this.features.push({\n type: 'circle',\n x, y, r\n })\n } else if (ap.type === 'R') { // 矩形\n let w = ap.params[0]\n let h = ap.params[1]\n if (this.state.unit === 'inch') { w *= 25.4; h *= 25.4 }\n \n const p1 = { x: x - w/2, y: y - h/2 }\n const p2 = { x: x + w/2, y: y - h/2 }\n const p3 = { x: x + w/2, y: y + h/2 }\n const p4 = { x: x - w/2, y: y + h/2 }\n this.addQuad(p1, p2, p3, p4)\n \n // 记录特征点(矩形)\n this.features.push({\n type: 'rect',\n x, y, w, h\n })\n } else if (ap.type === 'O') { // Obround(椭圆形/长圆形)\n let w = ap.params[0]\n let h = ap.params[1]\n if (this.state.unit === 'inch') { w *= 25.4; h *= 25.4 }\n \n // 渲染obround为两端半圆+中间矩形\n this.addObround(x, y, w, h)\n \n // 记录特征点(椭圆)\n this.features.push({\n type: 'obround',\n x, y, w, h\n })\n }\n }\n\n finishRegion() {\n const rawContours = this.currentRegionContours || []\n if (rawContours.length === 0) return\n\n // 清洗轮廓:去掉连续重复点、去掉闭合重复首点、去掉退化轮廓\n const cleanContour = (pts) => {\n if (!pts || pts.length === 0) return []\n const out = []\n let last = null\n for (const p of pts) {\n if (!last || Math.abs(p.x - last.x) > 1e-9 || Math.abs(p.y - last.y) > 1e-9) {\n out.push({ x: p.x, y: p.y })\n last = p\n }\n }\n if (out.length >= 2) {\n const first = out[0]\n const end = out[out.length - 1]\n if (Math.abs(first.x - end.x) < 1e-9 && Math.abs(first.y - end.y) < 1e-9) {\n out.pop()\n }\n }\n // 至少 3 个点\n if (out.length < 3) return []\n return out\n }\n\n const contours = rawContours.map(cleanContour).filter(c => c.length >= 3)\n if (contours.length === 0) return\n\n const signedArea = (pts) => {\n let a = 0\n for (let i = 0; i < pts.length; i++) {\n const p = pts[i]\n const q = pts[(i + 1) % pts.length]\n a += (p.x * q.y - q.x * p.y)\n }\n return a / 2\n }\n\n const bboxOf = (pts) => {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const p of pts) {\n minX = Math.min(minX, p.x)\n minY = Math.min(minY, p.y)\n maxX = Math.max(maxX, p.x)\n maxY = Math.max(maxY, p.y)\n }\n return { minX, minY, maxX, maxY }\n }\n\n const pointInPoly = (pt, poly) => {\n // Ray casting\n let inside = false\n for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {\n const xi = poly[i].x, yi = poly[i].y\n const xj = poly[j].x, yj = poly[j].y\n const intersect = ((yi > pt.y) !== (yj > pt.y)) &&\n (pt.x < (xj - xi) * (pt.y - yi) / ((yj - yi) || 1e-30) + xi)\n if (intersect) inside = !inside\n }\n return inside\n }\n\n // 构建包含关系(每个轮廓找最小的“父轮廓”)\n const metas = contours.map((pts, idx) => ({\n id: idx,\n pts,\n areaAbs: Math.abs(signedArea(pts)),\n bbox: bboxOf(pts),\n parent: -1,\n depth: 0,\n }))\n\n for (const a of metas) {\n const p = a.pts[0]\n let bestParent = null\n for (const b of metas) {\n if (a.id === b.id) continue\n // bbox 快速排除\n if (p.x < b.bbox.minX || p.x > b.bbox.maxX || p.y < b.bbox.minY || p.y > b.bbox.maxY) continue\n if (!pointInPoly(p, b.pts)) continue\n if (!bestParent || b.areaAbs < bestParent.areaAbs) {\n bestParent = b\n }\n }\n if (bestParent) a.parent = bestParent.id\n }\n\n // 计算深度\n const depthOf = (id) => {\n let d = 0\n let cur = metas[id]\n while (cur.parent !== -1) {\n d++\n cur = metas[cur.parent]\n }\n return d\n }\n for (const m of metas) m.depth = depthOf(m.id)\n\n // 每个 outer(深度为偶数)各自 triangulate,holes 取其直接子轮廓(深度=outer+1)\n const childrenMap = new Map()\n for (const m of metas) {\n if (!childrenMap.has(m.parent)) childrenMap.set(m.parent, [])\n childrenMap.get(m.parent).push(m.id)\n }\n\n const triangulateOne = (outerPts, holePtsList) => {\n const flat = []\n const holeIndices = []\n\n // outer\n for (const p of outerPts) flat.push(p.x, p.y)\n let cursor = outerPts.length\n\n // holes\n for (const holePts of holePtsList) {\n holeIndices.push(cursor)\n for (const p of holePts) flat.push(p.x, p.y)\n cursor += holePts.length\n }\n\n const triangles = earcut(flat, holeIndices.length ? holeIndices : null)\n if (!triangles || triangles.length === 0) return\n\n const baseIndex = this.vertices.length / 3\n for (let i = 0; i < flat.length; i += 2) {\n this.vertices.push(flat[i], flat[i + 1], 0)\n this.colors.push(1, 0, 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n this.triangleIndices.push(baseIndex + triangles[i])\n }\n }\n\n for (const m of metas) {\n if (m.depth % 2 !== 0) continue // holes 由 outer 处理\n const childIds = childrenMap.get(m.id) || []\n const directHoles = childIds\n .map(id => metas[id])\n .filter(c => c.depth === m.depth + 1) // 直接子层\n .map(c => c.pts)\n\n triangulateOne(m.pts, directHoles)\n }\n }\n\n addQuad(p1, p2, p3, p4) {\n const baseIndex = this.vertices.length / 3\n \n this.vertices.push(p1.x, p1.y, 0); this.colors.push(1, 0, 0)\n this.vertices.push(p2.x, p2.y, 0); this.colors.push(1, 0, 0)\n this.vertices.push(p3.x, p3.y, 0); this.colors.push(1, 0, 0)\n this.vertices.push(p4.x, p4.y, 0); this.colors.push(1, 0, 0)\n\n // 两个三角形组成矩形: 0-1-2, 0-2-3 (或者其他顺序,earcut顺时针)\n // p1(TL), p2(BL), p3(BR), p4(TR) -> assuming stroke direction logic\n // Let's use standard: 0,1,2 and 2,3,0\n this.triangleIndices.push(baseIndex, baseIndex+1, baseIndex+2)\n this.triangleIndices.push(baseIndex, baseIndex+2, baseIndex+3)\n }\n\n addCircle(cx, cy, r) {\n // 处理0半径\n if (r === 0) {\n r = 0.05 // 默认半径0.05mm\n }\n \n // 根据半径动态调整分段数(圆周长 / 0.2mm 作为分段数,保证足够圆滑)\n const circumference = 2 * Math.PI * r\n const segments = Math.min(Math.max(Math.ceil(circumference / 0.2), 12), 64)\n const baseIndex = this.vertices.length / 3\n \n this.vertices.push(cx, cy, 0) // Center\n this.colors.push(1, 0, 0)\n\n for (let i = 0; i <= segments; i++) {\n const theta = (i / segments) * Math.PI * 2\n this.vertices.push(cx + Math.cos(theta) * r, cy + Math.sin(theta) * r, 0)\n this.colors.push(1, 0, 0)\n }\n\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n }\n\n addObround(cx, cy, w, h) {\n // 处理0宽度/高度\n if (w === 0) w = 0.1\n if (h === 0) h = 0.1\n \n // Obround(椭圆形/长圆形)= 两端半圆 + 中间矩形\n if (w > h) {\n // 水平长圆形(宽度大于高度)\n const radius = h / 2\n const rectWidth = w - h // 中间矩形的宽度\n // 这里的 segments 是半圆的分段数\n const segments = Math.max(8, Math.ceil(radius * 4))\n \n // 左侧半圆中心\n const leftCx = cx - rectWidth / 2\n \n // 添加左半圆顶点 (PI/2 -> 3PI/2)\n let baseIndex = this.vertices.length / 3\n this.vertices.push(leftCx, cy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 90度 到 270度\n const angle = Math.PI/2 + (i / segments) * Math.PI\n this.vertices.push(leftCx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成左半圆三角形 (扇形)\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 中间矩形\n if (rectWidth > 0) {\n const p1 = { x: cx - rectWidth/2, y: cy - radius } // Top-Left\n const p2 = { x: cx - rectWidth/2, y: cy + radius } // Bottom-Left\n const p3 = { x: cx + rectWidth/2, y: cy + radius } // Bottom-Right\n const p4 = { x: cx + rectWidth/2, y: cy - radius } // Top-Right\n // 注意 addQuad 的参数顺序,需要确保是一个凸多边形顺序(如逆时针或顺时针)\n // 这里用: TL, BL, BR, TR\n this.addQuad(p1, p2, p3, p4)\n }\n \n // 右侧半圆中心\n const rightCx = cx + rectWidth / 2\n \n // 添加右半圆顶点 (-PI/2 -> PI/2)\n baseIndex = this.vertices.length / 3\n this.vertices.push(rightCx, cy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 -90度 到 90度\n const angle = -Math.PI/2 + (i / segments) * Math.PI\n this.vertices.push(rightCx + Math.cos(angle) * radius, cy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成右半圆三角形\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n } else {\n // 垂直长圆形(高度大于等于宽度)\n const radius = w / 2\n const rectHeight = h - w // 中间矩形的高度\n const segments = Math.max(8, Math.ceil(radius * 4))\n \n // 上侧半圆中心\n const topCy = cy + rectHeight / 2\n \n // 添加上半圆顶点 (0 -> PI)\n let baseIndex = this.vertices.length / 3\n this.vertices.push(cx, topCy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 0度 到 180度\n const angle = (i / segments) * Math.PI\n this.vertices.push(cx + Math.cos(angle) * radius, topCy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成上半圆三角形\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 中间矩形\n if (rectHeight > 0) {\n const p1 = { x: cx - radius, y: cy + rectHeight/2 } // Top-Left\n const p2 = { x: cx - radius, y: cy - rectHeight/2 } // Bottom-Left\n const p3 = { x: cx + radius, y: cy - rectHeight/2 } // Bottom-Right\n const p4 = { x: cx + radius, y: cy + rectHeight/2 } // Top-Right\n this.addQuad(p1, p2, p3, p4)\n }\n \n // 下侧半圆中心\n const bottomCy = cy - rectHeight / 2\n \n // 添加下半圆顶点 (PI -> 2PI)\n baseIndex = this.vertices.length / 3\n this.vertices.push(cx, bottomCy, 0) // 中心点\n this.colors.push(1, 0, 0)\n \n for (let i = 0; i <= segments; i++) {\n // 从 180度 到 360度\n const angle = Math.PI + (i / segments) * Math.PI\n this.vertices.push(cx + Math.cos(angle) * radius, bottomCy + Math.sin(angle) * radius, 0)\n this.colors.push(1, 0, 0)\n }\n \n // 生成下半圆三角形\n for (let i = 0; i < segments; i++) {\n this.triangleIndices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n }\n }\n}\n","import { GerberDirectParser } from './gerber-parser-direct.js'\nimport { extractRar } from '../../common/archiveExtractor.js'\nimport JSZip from 'jszip'\nimport { parse, plot, renderSVG } from 'web-gerber'\nimport earcut from 'earcut'\nimport { initUrlIdHandler, parseUrlId, parseUrlFileUrl } from '../../common/gerber-common.js'\n\n\nconst canvas = document.getElementById('canvas')\nconst fileInput = document.getElementById('fileInput')\nconst status = document.getElementById('status')\nconst fileInfo = document.getElementById('fileInfo')\nconst fileName = document.getElementById('fileName')\nconst fileSize = document.getElementById('fileSize')\nconst layerList = document.getElementById('layerList')\nconst layerToggleAll = document.getElementById('layerToggleAll')\nconst layerToggleAllCheckbox = document.getElementById('layerToggleAllCheckbox')\nconst loadingOverlay = document.getElementById('loadingOverlay')\n\n// 显示加载遮罩\nfunction showLoading() {\n if (loadingOverlay) loadingOverlay.classList.add('active')\n}\n\n// 隐藏加载遮罩\nfunction hideLoading() {\n if (loadingOverlay) loadingOverlay.classList.remove('active')\n}\n\n// WebGL Context\nlet gl = canvas.getContext('webgl', { \n stencil: true, // 启用模版缓冲,用于处理层内重叠\n alpha: false,\n preserveDrawingBuffer: true\n})\nlet program = null\nlet positionLocation\nlet uColorLocation\nlet uAlphaLocation\nlet uMatrixLocation\nlet quadVertexBuffer = null\nlet quadIndexBuffer = null\nconst IDENTITY_MATRIX = [\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n]\n// let vertexBuffer, colorBuffer, indexBuffer\n\n// 图层数据\nlet layers = [] // 存储每一层的几何数据和状态\nlet scale = 1, transX = 0, transY = 0\nlet isDragging = false\nlet dragStartX = 0, dragStartY = 0\nlet dragStartTransX = 0, dragStartTransY = 0\n\n// 渲染缓存\n// let cachedVertices = null\n// let cachedColors = null\n// let cachedIndices = null\nlet cacheNeedsUpdate = true\n\n// 颜色表(十六进制)\nconst baseColors = [\n '#FF0000', '#14FF00', '#0014FF', '#FFFF14', '#FF930C',\n '#B2FF0C', '#3CB2FF', '#C9B20A', '#B2C932', '#6432C9',\n '#93B20A', '#B27C52', '#7EC755', '#C73C7E', '#3778C7',\n '#9B9B40', '#7A409B', '#407A9B', '#7A9B40', '#583778',\n]\n\n// 默认图层透明度\nconst defaultLayerAlpha = 1.0\n\n// 取颜色:超出颜色表则用白色\nfunction pickColor(idx) {\n return idx < baseColors.length ? baseColors[idx] : '#FFFFFF'\n}\n\n// Solo 模式相关\nlet layerColorMap = new Map() // 存储图层颜色映射\nlet isSoloMode = false // 是否处于单独显示模式\nlet soloLayerOrder = [] // 存储solo模式下按勾选顺序排列的图层索引\nlet soloTargetLayerIndex = null // 存储双击的目标图层索引\n\nif (!gl) {\n alert('WebGL not supported')\n} else {\n initWebGL()\n animate()\n}\n\nfunction initWebGL() {\n const vsSource = `\n attribute vec3 a_position;\n uniform mat4 u_matrix;\n void main() {\n gl_Position = u_matrix * vec4(a_position, 1.0);\n }\n `\n const fsSource = `\n precision mediump float;\n uniform vec3 u_color;\n uniform float u_alpha;\n void main() {\n gl_FragColor = vec4(u_color, u_alpha);\n }\n `\n \n const ext = gl.getExtension('OES_element_index_uint')\n if (!ext) {\n console.warn('OES_element_index_uint not supported')\n }\n \n const vs = createShader(gl, gl.VERTEX_SHADER, vsSource)\n const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource)\n program = createProgram(gl, vs, fs)\n \n positionLocation = gl.getAttribLocation(program, 'a_position')\n uColorLocation = gl.getUniformLocation(program, 'u_color')\n uAlphaLocation = gl.getUniformLocation(program, 'u_alpha')\n uMatrixLocation = gl.getUniformLocation(program, 'u_matrix')\n\n // fullscreen quad(用于基于 stencil mask 的上色)\n quadVertexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, quadVertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([\n -1, -1, 0,\n 1, -1, 0,\n 1, 1, 0,\n -1, 1, 0\n ]), gl.STATIC_DRAW)\n\n quadIndexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer)\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW)\n \n // 移除全局 buffer,改用按图层管理的 buffer\n // vertexBuffer = gl.createBuffer()\n // colorBuffer = gl.createBuffer()\n // indexBuffer = gl.createBuffer()\n \n gl.enable(gl.BLEND)\n // 使用加法混合模式 (Additive Blending) 实现颜色叠加 (红+绿=黄)\n gl.blendFunc(gl.SRC_ALPHA, gl.ONE)\n \n resize()\n // 启用 resize 监听,避免窗口变化时画布变形\n window.addEventListener('resize', resize)\n \n canvas.style.cursor = 'grab'\n \n canvas.addEventListener('mousedown', e => {\n if (e.button !== 0) return // 只响应左键\n isDragging = true\n dragStartX = e.clientX\n dragStartY = e.clientY\n dragStartTransX = transX\n dragStartTransY = transY\n canvas.style.cursor = 'grabbing'\n })\n \n window.addEventListener('mousemove', e => {\n if (!isDragging) return\n \n const deltaX = e.clientX - dragStartX\n const deltaY = e.clientY - dragStartY\n const rect = canvas.getBoundingClientRect()\n \n // 将像素偏移转换为归一化设备坐标(NDC)偏移\n const normalizedDeltaX = (deltaX / rect.width) * 2\n const normalizedDeltaY = -(deltaY / rect.height) * 2\n \n transX = dragStartTransX + normalizedDeltaX\n transY = dragStartTransY + normalizedDeltaY\n })\n \n window.addEventListener('mouseup', () => {\n if (isDragging) {\n isDragging = false\n canvas.style.cursor = 'grab'\n }\n })\n \n canvas.addEventListener('mouseleave', () => {\n if (isDragging) {\n isDragging = false\n canvas.style.cursor = 'grab'\n }\n })\n canvas.addEventListener('wheel', e => {\n e.preventDefault()\n \n // 获取鼠标在画布上的位置\n const rect = canvas.getBoundingClientRect()\n const mouseX = e.clientX - rect.left\n const mouseY = e.clientY - rect.top\n \n // 归一化鼠标坐标到 [-1, 1]\n const normalizedX = (mouseX / rect.width) * 2 - 1\n const normalizedY = -((mouseY / rect.height) * 2 - 1)\n \n const canvasAspect = rect.width / rect.height\n \n // 计算鼠标位置在世界坐标系中的坐标(缩放前)\n // 逆向变换:NDC -> 世界坐标\n const worldXBefore = (normalizedX - transX) / (scale / canvasAspect)\n const worldYBefore = (normalizedY - transY) / scale\n \n // 缩放\n const scaleFactor = e.deltaY > 0 ? 0.9 : 1.1\n scale *= scaleFactor\n \n // 计算新的平移,使得鼠标指向的世界坐标点保持不变\n // 世界坐标 -> NDC:x_ndc = x_world * scale / aspect + transX\n // 所以:transX = x_ndc - x_world * scale / aspect\n transX = normalizedX - worldXBefore * (scale / canvasAspect)\n transY = normalizedY - worldYBefore * scale\n })\n}\n\nfunction createShader(gl, type, source) {\n const shader = gl.createShader(type)\n gl.shaderSource(shader, source)\n gl.compileShader(shader)\n if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n console.error(gl.getShaderInfoLog(shader))\n gl.deleteShader(shader)\n return null\n }\n return shader\n}\n\nfunction createProgram(gl, vs, fs) {\n const p = gl.createProgram()\n gl.attachShader(p, vs)\n gl.attachShader(p, fs)\n gl.linkProgram(p)\n return p\n}\n\nfunction resize() {\n const oldWidth = canvas.width\n const oldHeight = canvas.height\n \n canvas.width = canvas.clientWidth\n canvas.height = canvas.clientHeight\n gl.viewport(0, 0, canvas.width, canvas.height)\n \n // 保持中心点位置不变\n if (oldWidth > 0 && oldHeight > 0 && layers.length > 0) {\n const oldAspect = oldWidth / oldHeight\n const newAspect = canvas.width / canvas.height\n \n // transX 是 NDC 坐标系下的平移量\n // 我们的目标是保持 worldSpace 下的中心点在屏幕上的位置相对不变\n // worldX = (ndcX - transX) / (scale / aspect)\n // ndcX = 0 (屏幕中心) -> centerWorldX = -transX / (scale / oldAspect)\n \n // 我们希望新的 transX' 使得 centerWorldX 对应屏幕中心 (ndc=0)\n // 0 = centerWorldX * (scale / newAspect) + transX'\n // transX' = -centerWorldX * scale / newAspect\n // = -(-transX / (scale / oldAspect)) * scale / newAspect\n // = transX * (scale / oldAspect) * scale / newAspect / scale (错误推导)\n \n // 重新推导:\n // centerWorldX = -transX * oldAspect / scale\n // newTransX = -centerWorldX * scale / newAspect\n // = -(-transX * oldAspect / scale) * scale / newAspect\n // = transX * oldAspect / newAspect\n \n transX = transX * (oldAspect / newAspect)\n }\n\n // 如果已经有图层数据,立即重新渲染\n if (layers.length > 0) {\n cacheNeedsUpdate = true\n requestAnimationFrame(() => {\n if (typeof render === 'function') {\n render()\n }\n })\n }\n}\n\n// ==================== 工具函数 ====================\n\n/**\n * 十六进制颜色转 RGB\n */\nfunction hexToRgb(hex) {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex)\n return result ? {\n r: parseInt(result[1], 16) / 255,\n g: parseInt(result[2], 16) / 255,\n b: parseInt(result[3], 16) / 255\n } : { r: 1, g: 0, b: 0 }\n}\n\n/**\n * 使用 web-gerber 解析 Gerber 和钻孔文件\n */\n// 从同一包里的 Gerber 文件(如 GTL/GBL)提取 %FS/%MO 作为 Excellon(TXT/DRL) 的格式兜底。\n// 目的:当钻孔文件没有显式 FILE_FORMAT/LZ/TZ 时,避免 web-gerber 误判小数点导致孔位“放大10/100倍”或错位。\nlet inheritedGerberFormatHint = null // { integerPlaces, decimalPlaces, places:[int,dec], zero:'L'|'T', unit:'in'|'mm', from:string }\n\nfunction updateInheritedGerberFormatHintFromText(text, fileName) {\n if (!text || typeof text !== 'string') return\n // 只从 Gerber 中提取(排除钻孔:M48)\n if (/\\bM48\\b/i.test(text)) return\n\n const fsMatch = text.match(/%FS([LT])?[AI]X(\\d)(\\d)Y(\\d)(\\d)\\*%/i)\n if (!fsMatch) return\n\n const zero = ((fsMatch[1] || 'L') + '').toUpperCase() === 'T' ? 'T' : 'L'\n const integerPlaces = parseInt(fsMatch[2], 10)\n const decimalPlaces = parseInt(fsMatch[3], 10)\n if (!Number.isFinite(integerPlaces) || !Number.isFinite(decimalPlaces)) return\n\n let unit = null\n const moMatch = text.match(/%MO(IN|MM)\\*%/i)\n if (moMatch) {\n unit = moMatch[1].toUpperCase() === 'IN' ? 'in' : 'mm'\n } else {\n if (/%MOIN\\*%/i.test(text) || /\\bG70\\b/.test(text)) unit = 'in'\n if (/%MOMM\\*%/i.test(text) || /\\bG71\\b/.test(text)) unit = 'mm'\n }\n\n inheritedGerberFormatHint = {\n integerPlaces,\n decimalPlaces,\n totalPlaces: integerPlaces + decimalPlaces,\n places: [integerPlaces, decimalPlaces],\n zero,\n unit,\n from: fileName\n }\n console.log(\n `[格式兜底] 从 ${fileName} 提取 Gerber 格式: ${zero === 'L' ? 'LZ' : 'TZ'} ${integerPlaces}:${decimalPlaces} (unit=${unit || 'unknown'})`\n )\n}\n\nasync function parseWithWebGerber(text, fileName) {\n try {\n console.log(`[web-gerber] 开始解析 ${fileName}`)\n \n // 判断文件类型(钻孔文件含 M48,普通 Gerber 文件不含)\n const isDrillFile = /\\bM48\\b/i.test(text)\n const fileType = isDrillFile ? 'drill' : 'gerber'\n console.log(`[web-gerber] 文件类型: ${fileType}`)\n \n // 对钻孔文件进行格式检测和修正\n let detectedFormat = null\n let drillUnits = null // 'mm' | 'in' | null\n let drillZero = null // 'LZ' | 'TZ' | null\n if (isDrillFile) {\n // 提取格式信息(FILE_FORMAT=X:Y)\n const lines = text.split('\\n').map(line => line.trim())\n \n // 单位/零抑制(Excellon 常见:METRIC,LZ / INCH,TZ / M71 / M72)\n for (const line of lines) {\n if (!drillUnits) {\n if (/^METRIC\\b/i.test(line) || /^M71\\b/i.test(line)) drillUnits = 'mm'\n if (/^INCH\\b/i.test(line) || /^M72\\b/i.test(line)) drillUnits = 'in'\n }\n if (!drillZero) {\n if (/\\bLZ\\b/i.test(line)) drillZero = 'LZ'\n if (/\\bTZ\\b/i.test(line)) drillZero = 'TZ'\n }\n if (drillUnits && drillZero) break\n }\n \n for (const line of lines) {\n const formatMatch = line.match(/FILE_FORMAT[=:](\\d+)[:.](\\d+)/i)\n if (formatMatch) {\n const integerPlaces = parseInt(formatMatch[1])\n const decimalPlaces = parseInt(formatMatch[2])\n detectedFormat = {\n integerPlaces,\n decimalPlaces,\n totalPlaces: integerPlaces + decimalPlaces,\n places: [integerPlaces, decimalPlaces]\n }\n console.log(`[web-gerber] 检测到格式: ${integerPlaces}:${decimalPlaces} (${integerPlaces}位整数, ${decimalPlaces}位小数)`)\n break\n }\n }\n\n // 坐标位数统计(即使没有 FILE_FORMAT 也做),用于推断/兜底\n let maxDigits = 0\n const coordMatchesAll = text.match(/[XY](-?\\d+)/g)\n if (coordMatchesAll) {\n for (const coord of coordMatchesAll) {\n const digits = coord.substring(1).replace(/-/g, '').length\n maxDigits = Math.max(maxDigits, digits)\n }\n }\n\n // 如果 drill 文件没有声明 FILE_FORMAT,则优先继承同包 Gerber 的 FS 格式;否则用坐标位数推断\n if (!detectedFormat) {\n if (inheritedGerberFormatHint && inheritedGerberFormatHint.places) {\n detectedFormat = {\n integerPlaces: inheritedGerberFormatHint.integerPlaces,\n decimalPlaces: inheritedGerberFormatHint.decimalPlaces,\n totalPlaces: inheritedGerberFormatHint.integerPlaces + inheritedGerberFormatHint.decimalPlaces,\n places: [inheritedGerberFormatHint.integerPlaces, inheritedGerberFormatHint.decimalPlaces]\n }\n console.warn(\n `[web-gerber] Drill未声明FILE_FORMAT,继承 Gerber(${inheritedGerberFormatHint.from}) 格式: ${detectedFormat.integerPlaces}:${detectedFormat.decimalPlaces}`\n )\n } else if (maxDigits > 0) {\n // heuristic:优先假设 2 位整数(常见 Excellon/Gerber),其余为小数位\n const integerPlaces = Math.min(2, maxDigits)\n const decimalPlaces = Math.max(0, maxDigits - integerPlaces)\n detectedFormat = {\n integerPlaces,\n decimalPlaces,\n totalPlaces: integerPlaces + decimalPlaces,\n places: [integerPlaces, decimalPlaces]\n }\n console.warn(\n `[web-gerber] Drill未声明FILE_FORMAT,按坐标位数推断格式: ${integerPlaces}:${decimalPlaces} (maxDigits=${maxDigits})`\n )\n }\n }\n\n // 钻孔文件未声明单位/零抑制时,尝试继承 Gerber 的 MO/FS\n if (!drillUnits && inheritedGerberFormatHint && inheritedGerberFormatHint.unit) {\n drillUnits = inheritedGerberFormatHint.unit === 'in' ? 'in' : 'mm'\n }\n if (!drillZero && inheritedGerberFormatHint && inheritedGerberFormatHint.zero) {\n drillZero = inheritedGerberFormatHint.zero === 'T' ? 'TZ' : 'LZ'\n }\n \n // 动态格式检测:检查实际坐标位数\n if (detectedFormat) {\n const coordMatches = coordMatchesAll\n if (coordMatches) {\n // maxDigits 已计算\n \n const definedDigits = detectedFormat.totalPlaces\n console.log(`[web-gerber] 定义格式: [${detectedFormat.integerPlaces}, ${detectedFormat.decimalPlaces}] (${definedDigits}位)`)\n console.log(`[web-gerber] 实际最长坐标: ${maxDigits}位`)\n \n // 如果实际坐标位数超过定义位数,动态增加整数位\n if (maxDigits > definedDigits) {\n const extraDigits = maxDigits - definedDigits\n detectedFormat.integerPlaces += extraDigits\n detectedFormat.totalPlaces = detectedFormat.integerPlaces + detectedFormat.decimalPlaces\n detectedFormat.places = [detectedFormat.integerPlaces, detectedFormat.decimalPlaces]\n console.log(`[web-gerber] ✓ 动态调整格式为: [${detectedFormat.integerPlaces}, ${detectedFormat.decimalPlaces}] (${maxDigits}位)`)\n \n // 修改文件内容中的 FILE_FORMAT 定义\n const oldFormatPattern = /FILE_FORMAT[=:]\\d+[:.]\\d+/i\n const newFormatStr = `FILE_FORMAT=${detectedFormat.integerPlaces}:${detectedFormat.decimalPlaces}`\n \n if (oldFormatPattern.test(text)) {\n text = text.replace(oldFormatPattern, newFormatStr)\n console.log(`[web-gerber] ✓ 已修改文件格式定义 → ${newFormatStr}`)\n }\n }\n }\n }\n }\n \n // 使用 web-gerber 解析(传入修正后的文本和文件类型)\n let parseResult = null\n if (isDrillFile && detectedFormat && detectedFormat.places) {\n // 优先用显式格式解析钻孔(避免小数点位数被库误判)\n const zeroChar = (drillZero || 'LZ').toUpperCase() === 'TZ' ? 'T' : 'L'\n const parseOpts = {\n // 兼容不同版本的 web-gerber:同时提供 type/filetype\n type: 'drill',\n filetype: 'drill',\n format: { places: detectedFormat.places, zero: zeroChar }\n }\n try {\n console.log(\n `[web-gerber] Drill使用显式format解析: places=[${detectedFormat.places[0]},${detectedFormat.places[1]}], zero=${zeroChar}, units=${drillUnits || 'auto'}`\n )\n parseResult = parse(text, parseOpts)\n } catch (e) {\n console.warn(`[web-gerber] Drill显式format解析失败,回退默认解析: ${e?.message || e}`)\n parseResult = null\n }\n }\n if (!parseResult) {\n parseResult = parse(text, { filetype: fileType })\n }\n const plotResult = plot(parseResult)\n \n // console.log(`[web-gerber] plotResult:`, plotResult)\n console.log(`[web-gerber] plotResult.children 数量:`, plotResult.children?.length)\n \n // 使用 renderSVG 将 plotResult 转换成 SVG 树(宏定义会被正确展开)\n const svgTree = renderSVG(plotResult)\n console.log(`[web-gerber] SVG树生成完成, children 数量:`, svgTree?.children?.length)\n\n // 确定是否为轮廓层(GKO、GM1 等)- 检查文件扩展名和文件名\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // 检查扩展名和文件名关键词\n const isOutlineLayer = \n fileExtension === '.GKO' || \n fileExtension === '.GM' || \n /^\\.GM\\d+$/i.test(fileExtension) ||\n fileNameUpper.includes('OUTLINE') ||\n /EDGE[._-]CUTS/i.test(fileNameUpper) ||\n fileNameUpper.includes('WX') ||\n fileNameUpper.includes('BOARD') ||\n fileNameUpper.startsWith('GKO') ||\n fileNameUpper.startsWith('OUT')\n \n // 轮廓层最小线宽 0.5mm,确保可见\n const minWidth = isOutlineLayer ? 0.3 : 0\n \n // 轮廓层打印识别日志\n if (isOutlineLayer) {\n console.log(`[轮廓层] ${fileNameOnly}: minWidth=${minWidth}mm`)\n }\n \n const vertices = []\n const indices = []\n // drillPosScaleRuns:仅用于“钻孔坐标位数/零抑制误解读”导致的整体缩放错位时,\n // 后续按参考层 bounds 进行“只缩放孔位、不缩放孔径”的修正。\n // 记录每个 circle/rectangle 等由“中心+尺寸”生成的顶点区间,便于只移动中心而不缩放半径/孔径。\n const drillPosScaleRuns = isDrillFile ? [] : null\n // polarityRuns:记录 indices 的区间 [start,end),用于渲染阶段按 LPC/LPD 做 stencil 挖空\n // 初始默认为 dark(Gerber 默认极性)\n const polarityRuns = []\n let currentPolarity = 'dark'\n let currentRunStart = 0\n const ensurePolarity = (nextPolarity) => {\n const p = (nextPolarity || 'dark')\n if (p === currentPolarity) return\n if (indices.length > currentRunStart) {\n polarityRuns.push({ polarity: currentPolarity, start: currentRunStart, end: indices.length })\n }\n currentPolarity = p\n currentRunStart = indices.length\n }\n const scale = plotResult.units === 'in' ? 25.4 : 1 // 英寸转毫米\n \n // ==================== Drill: FILE_FORMAT 坐标缩放修正(只修正位置,不放大孔径)====================\n // 部分 Excellon 会用注释 `;FILE_FORMAT=4:4` 指定隐式小数点位数,但解析器可能按默认格式解释坐标,\n // 导致所有孔位 XY 缩小 10/100 倍,看起来“只渲染出一个点”。\n // 这里:用首个 X/Y 数字串按 detectedFormat 推导期望坐标,再与 plotResult.children 的首个 circle.cx/cy 对比,\n // 得到一个 posScaleFix(用于坐标位置)。\n let coordPosScaleFix = 1\n const parseFixedCoord = (rawDigits, fmt, zeroMode) => {\n if (!fmt || rawDigits == null) return null\n const s = String(rawDigits).trim()\n const sign = s.startsWith('-') ? -1 : 1\n let digits = s.replace(/[^0-9]/g, '')\n if (!digits) return null\n const need = fmt.totalPlaces\n if (need && digits.length < need) {\n // LZ (Leading Zero suppression) = 前导零被省略 → 需要左侧补零\n // TZ (Trailing Zero suppression) = 尾部零被省略 → 需要右侧补零\n if ((zeroMode || '').toUpperCase() === 'LZ') {\n digits = digits.padStart(need, '0') // LZ: 左侧补零\n } else {\n digits = digits.padEnd(need, '0') // TZ: 右侧补零\n }\n }\n const denom = Math.pow(10, fmt.decimalPlaces || 0)\n const n = parseInt(digits, 10)\n if (!Number.isFinite(n) || denom === 0) return null\n return sign * (n / denom)\n }\n\n if (isDrillFile && detectedFormat && Array.isArray(plotResult.children) && plotResult.children.length > 0) {\n try {\n const xyMatch = text.match(/X(-?\\d+)\\s*Y(-?\\d+)/i)\n const firstCircle = plotResult.children.find(c =>\n c?.type === 'imageShape' &&\n c.shape?.type === 'circle' &&\n Number.isFinite(c.shape.cx) &&\n Number.isFinite(c.shape.cy)\n )\n\n if (xyMatch && firstCircle) {\n const actX = firstCircle.shape.cx\n const actY = firstCircle.shape.cy\n\n // 将期望坐标转换到 plotResult.units(in/mm)\n const plotUnits = plotResult.units === 'in' ? 'in' : 'mm'\n const srcUnits = drillUnits || plotUnits\n\n const evalZeroMode = (zeroMode) => {\n let expX = parseFixedCoord(xyMatch[1], detectedFormat, zeroMode)\n let expY = parseFixedCoord(xyMatch[2], detectedFormat, zeroMode)\n if (expX != null && expY != null) {\n if (srcUnits === 'in' && plotUnits === 'mm') { expX *= 25.4; expY *= 25.4 }\n if (srcUnits === 'mm' && plotUnits === 'in') { expX /= 25.4; expY /= 25.4 }\n }\n const ratios = []\n if (Number.isFinite(expX) && actX !== 0) ratios.push(expX / actX)\n if (Number.isFinite(expY) && actY !== 0) ratios.push(expY / actY)\n if (ratios.length === 0) return null\n const avg = ratios.reduce((s, v) => s + v, 0) / ratios.length\n const max = Math.max(...ratios)\n const min = Math.min(...ratios)\n const spread = (avg !== 0) ? Math.abs(max - min) / Math.abs(avg) : 999\n return { zeroMode, expX, expY, avg, spread }\n }\n\n // 某些 Excellon 头会写 INCH,LZ 但实际更像 TZ(或反之),\n // 如果我们只信 header 去推导 expected,会误触发 posScaleFix 把孔缩到角落。\n const primaryZero = (drillZero || '').toUpperCase() === 'TZ' ? 'TZ' : 'LZ'\n const altZero = primaryZero === 'TZ' ? 'LZ' : 'TZ'\n const primary = evalZeroMode(primaryZero)\n const alt = evalZeroMode(altZero)\n\n const near1 = (r) => r && Number.isFinite(r.avg) && r.spread < 0.05 && Math.abs(r.avg - 1) <= 0.05\n\n if (near1(primary)) {\n // 已匹配,无需缩放\n } else if (near1(alt)) {\n console.warn(`[web-gerber] ⚠️ Drill零抑制声明为 ${primaryZero},但坐标更符合 ${altZero}(ratio≈1),跳过 posScaleFix 以避免误缩放`)\n } else {\n // 两种零抑制都不接近 1,才认为可能存在“整体倍率错误”,按 header 优先推断\n const chosen = primary || alt\n if (chosen && Number.isFinite(chosen.avg) && Math.abs(chosen.avg - 1) > 0.05 && chosen.spread < 0.05) {\n const absAvg = Math.abs(chosen.avg)\n const pow10 = Math.pow(10, Math.round(Math.log10(absAvg)))\n const snapped = (Math.abs(absAvg - pow10) / pow10) < 0.15 ? pow10 : absAvg\n if (snapped >= 0.01 && snapped <= 10000) {\n coordPosScaleFix = snapped\n console.warn(`[web-gerber] ⚠️ Drill坐标格式疑似被错误解释,启用 FILE_FORMAT 坐标缩放修正:posScaleFix=${coordPosScaleFix} (units=${plotUnits}, zero=${chosen.zeroMode})`)\n }\n }\n }\n }\n } catch (e) {\n console.warn('[web-gerber] Drill FILE_FORMAT 坐标缩放修正失败,忽略:', e?.message || e)\n }\n }\n\n const posScale = scale * coordPosScaleFix // 仅用于坐标“位置”\n const sizeScale = scale // 用于孔径/线宽等“尺寸”(不要乘 posScaleFix)\n \n /**\n * 从 SVG 树提取元素并转换为实心三角形\n */\n const processSvgElement = (element) => {\n if (!element || !element.tagName) return\n \n const tagName = element.tagName.toLowerCase()\n \n // 处理圆形 → 三角扇形(实心)\n if (tagName === 'circle') {\n const cx = parseFloat(element.properties?.cx || element.attributes?.cx || 0) * scale\n const cy = parseFloat(element.properties?.cy || element.attributes?.cy || 0) * scale * (-1) // 翻转Y轴\n const r = parseFloat(element.properties?.r || element.attributes?.r || 0) * scale\n \n if (r > 0) {\n const segments = Math.min(Math.max(Math.ceil(2 * Math.PI * r / 0.2), 12), 64)\n const baseIndex = vertices.length / 3\n \n // 中心点\n vertices.push(cx, cy, 0)\n \n // 圆周点\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2\n vertices.push(cx + Math.cos(angle) * r, cy + Math.sin(angle) * r, 0)\n }\n \n // 三角形索引(扇形)\n for (let i = 0; i < segments; i++) {\n indices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 额外添加10个中心点(用于特征提取,确保顶点簇检测能识别)\n for (let i = 0; i < 10; i++) {\n vertices.push(cx, cy, 0)\n }\n }\n }\n // 处理椭圆 → 三角扇形(实心)\n else if (tagName === 'ellipse') {\n const cx = parseFloat(element.properties?.cx || element.attributes?.cx || 0) * scale\n const cy = parseFloat(element.properties?.cy || element.attributes?.cy || 0) * scale * (-1) // 翻转Y轴\n const rx = parseFloat(element.properties?.rx || element.attributes?.rx || 0) * scale\n const ry = parseFloat(element.properties?.ry || element.attributes?.ry || 0) * scale\n \n if (rx > 0 && ry > 0) {\n const segments = Math.min(Math.max(Math.ceil(2 * Math.PI * Math.max(rx, ry) / 0.2), 12), 64)\n const baseIndex = vertices.length / 3\n \n // 中心点\n vertices.push(cx, cy, 0)\n \n // 椭圆周点\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2\n vertices.push(cx + Math.cos(angle) * rx, cy + Math.sin(angle) * ry, 0)\n }\n \n // 三角形索引(扇形)\n for (let i = 0; i < segments; i++) {\n indices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 额外添加10个中心点(用于特征提取)\n for (let i = 0; i < 10; i++) {\n vertices.push(cx, cy, 0)\n }\n }\n }\n // 处理矩形 → 2个三角形(实心)或圆角矩形\n else if (tagName === 'rect') {\n const x = parseFloat(element.properties?.x || element.attributes?.x || 0) * scale\n const y = parseFloat(element.properties?.y || element.attributes?.y || 0) * scale * (-1) // 翻转Y轴\n const width = parseFloat(element.properties?.width || element.attributes?.width || 0) * scale\n const height = parseFloat(element.properties?.height || element.attributes?.height || 0) * scale\n const rx = parseFloat(element.properties?.rx || element.attributes?.rx || 0) * scale\n const ry = parseFloat(element.properties?.ry || element.attributes?.ry || 0) * scale\n \n if (width > 0 && height > 0) {\n // 检查是否有圆角(Obround)\n if (rx > 0 || ry > 0) {\n // 圆角矩形 - 渲染成药丸形状(中间矩形 + 两端半圆)\n const cornerRadius = Math.max(rx, ry)\n \n // 判断方向:横向还是纵向\n if (width > height) {\n // 横向长圆形(两端半圆在左右)\n const radius = height / 2\n const rectWidth = width - height // 中间矩形的宽度\n \n // 左半圆\n const leftCx = x + radius\n const leftCy = y - radius\n const segments = Math.max(12, Math.ceil(radius * 2))\n let centerIdx = vertices.length / 3\n vertices.push(leftCx, leftCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(leftCx + Math.cos(angle) * radius, leftCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n \n // 中间矩形\n if (rectWidth > 0) {\n const baseIndex = vertices.length / 3\n vertices.push(x + radius, y, 0)\n vertices.push(x + radius + rectWidth, y, 0)\n vertices.push(x + radius + rectWidth, y - height, 0)\n vertices.push(x + radius, y - height, 0)\n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n }\n \n // 右半圆\n const rightCx = x + width - radius\n const rightCy = y - radius\n centerIdx = vertices.length / 3\n vertices.push(rightCx, rightCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(rightCx + Math.cos(angle) * radius, rightCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n } else {\n // 纵向长圆形(两端半圆在上下)\n const radius = width / 2\n const rectHeight = height - width // 中间矩形的高度\n \n // 上半圆\n const topCx = x + radius\n const topCy = y - radius\n const segments = Math.max(12, Math.ceil(radius * 2))\n let centerIdx = vertices.length / 3\n vertices.push(topCx, topCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(topCx + Math.cos(angle) * radius, topCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n \n // 中间矩形\n if (rectHeight > 0) {\n const baseIndex = vertices.length / 3\n vertices.push(x, y - radius, 0)\n vertices.push(x + width, y - radius, 0)\n vertices.push(x + width, y - radius - rectHeight, 0)\n vertices.push(x, y - radius - rectHeight, 0)\n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n }\n \n // 下半圆\n const bottomCx = x + radius\n const bottomCy = y - height + radius\n centerIdx = vertices.length / 3\n vertices.push(bottomCx, bottomCy, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(bottomCx + Math.cos(angle) * radius, bottomCy + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n } else {\n // 普通矩形(无圆角)\n const baseIndex = vertices.length / 3\n // 注意:Y轴翻转后,需要调整矩形的绘制顺序\n vertices.push(x, y, 0)\n vertices.push(x + width, y, 0)\n vertices.push(x + width, y - height, 0) // y - height 因为Y轴已翻转\n vertices.push(x, y - height, 0)\n \n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n }\n }\n }\n // 处理路径(宏定义展开的复杂形状)\n else if (tagName === 'path') {\n const d = element.properties?.d || element.attributes?.d || ''\n const fill = element.properties?.fill || element.attributes?.fill || ''\n const stroke = element.properties?.stroke || element.attributes?.stroke || ''\n \n // 判断是填充路径还是线条路径\n const isFillPath = fill && fill !== 'none'\n const isStrokePath = stroke && stroke !== 'none'\n \n if (d) {\n const pathPoints = parseSvgPath(d, scale)\n \n // 如果是填充路径 → earcut 三角化(实心)\n if (isFillPath && pathPoints && pathPoints.length >= 6) {\n try {\n const triangles = earcut(pathPoints)\n if (triangles && triangles.length > 0) {\n const baseIndex = vertices.length / 3\n for (let i = 0; i < pathPoints.length; i += 2) {\n vertices.push(pathPoints[i], pathPoints[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n } catch (e) {\n console.warn('[SVG Path Fill] 三角化失败:', e)\n }\n }\n // 如果是线条路径 → 生成带宽度的线段 + 圆形端点\n else if (isStrokePath && pathPoints && pathPoints.length >= 4) {\n let strokeWidth = parseFloat(element.properties?.['stroke-width'] || element.attributes?.['stroke-width'] || 0.15) * scale\n if (strokeWidth < minWidth) strokeWidth = minWidth\n \n const radius = strokeWidth / 2\n \n // 将路径转换为一系列线段 + 端点\n for (let i = 0; i < pathPoints.length - 2; i += 2) {\n const x1 = pathPoints[i]\n const y1 = pathPoints[i + 1]\n const x2 = pathPoints[i + 2]\n const y2 = pathPoints[i + 3]\n \n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n \n if (len > 0.001) { // 忽略极短的线段\n const nx = -dy / len * radius\n const ny = dx / len * radius\n \n // 绘制线段主体(矩形)\n const baseIndex = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex + 1, baseIndex + 3, baseIndex + 2)\n \n // 在起点添加圆形端点(只在第一个线段添加)\n if (i === 0) {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(x1, y1, 0) // 中心点\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x1 + Math.cos(angle) * radius, y1 + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 在终点添加圆形端点(每个线段的终点)\n {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(x2, y2, 0) // 中心点\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x2 + Math.cos(angle) * radius, y2 + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n }\n }\n }\n }\n // 处理线条 → 带宽度的矩形 + 圆形端点(实心)\n else if (tagName === 'line') {\n const x1 = parseFloat(element.properties?.x1 || element.attributes?.x1 || 0) * scale\n const y1 = parseFloat(element.properties?.y1 || element.attributes?.y1 || 0) * scale * (-1) // 翻转Y轴\n const x2 = parseFloat(element.properties?.x2 || element.attributes?.x2 || 0) * scale\n const y2 = parseFloat(element.properties?.y2 || element.attributes?.y2 || 0) * scale * (-1) // 翻转Y轴\n \n // 从 SVG 属性中获取线宽\n let strokeWidth = parseFloat(element.properties?.['stroke-width'] || element.attributes?.['stroke-width'] || 0.15) * scale\n \n // 应用最小线宽\n if (strokeWidth < minWidth) strokeWidth = minWidth\n \n const radius = strokeWidth / 2\n \n // 绘制矩形线段\n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len > 0) {\n const nx = -dy / len * radius\n const ny = dx / len * radius\n \n // 线段主体\n const baseIndex = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex + 1, baseIndex + 3, baseIndex + 2)\n \n // 起点圆形端点\n const segments = Math.max(8, Math.ceil(radius * 2))\n let centerIdx = vertices.length / 3\n vertices.push(x1, y1, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x1 + Math.cos(angle) * radius, y1 + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n \n // 终点圆形端点\n centerIdx = vertices.length / 3\n vertices.push(x2, y2, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x2 + Math.cos(angle) * radius, y2 + Math.sin(angle) * radius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n // 处理多段线 polyline\n else if (tagName === 'polyline' || tagName === 'polygon') {\n const pointsStr = element.properties?.points || element.attributes?.points || ''\n if (pointsStr) {\n const coords = pointsStr.trim().split(/[\\s,]+/).map(parseFloat)\n if (coords.length >= 6) { // 至少3个点\n const pathPoints = []\n for (let i = 0; i < coords.length; i += 2) {\n if (!isNaN(coords[i]) && !isNaN(coords[i + 1])) {\n pathPoints.push(coords[i] * scale, coords[i + 1] * scale * (-1)) // 翻转Y轴\n }\n }\n \n if (pathPoints.length >= 6) {\n try {\n const triangles = earcut(pathPoints)\n if (triangles && triangles.length > 0) {\n const baseIndex = vertices.length / 3\n for (let i = 0; i < pathPoints.length; i += 2) {\n vertices.push(pathPoints[i], pathPoints[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n } catch (e) {\n console.warn('[SVG Polyline] 三角化失败:', e)\n }\n }\n }\n }\n }\n // 递归处理子元素(group 等容器)\n if (element.children && element.children.length > 0) {\n element.children.forEach(child => processSvgElement(child))\n }\n }\n \n /**\n * 简化的 SVG 路径解析器\n */\n function parseSvgPath(d, scale) {\n const coords = []\n const commands = d.match(/[MLHVCSQTAZ][^MLHVCSQTAZ]*/gi) || []\n let currentX = 0, currentY = 0\n let startX = 0, startY = 0\n \n commands.forEach(cmd => {\n const type = cmd[0].toUpperCase()\n const isRelative = cmd[0] === cmd[0].toLowerCase()\n const args = cmd.slice(1).trim().split(/[\\s,]+/).filter(s => s).map(parseFloat)\n \n switch (type) {\n case 'M': // Move\n if (args.length >= 2) {\n currentX = isRelative ? currentX + args[0] : args[0]\n currentY = isRelative ? currentY + args[1] : args[1]\n startX = currentX\n startY = currentY\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'L': // Line\n if (args.length >= 2) {\n currentX = isRelative ? currentX + args[0] : args[0]\n currentY = isRelative ? currentY + args[1] : args[1]\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'H': // Horizontal line\n if (args.length >= 1) {\n currentX = isRelative ? currentX + args[0] : args[0]\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'V': // Vertical line\n if (args.length >= 1) {\n currentY = isRelative ? currentY + args[0] : args[0]\n coords.push(currentX * scale, currentY * scale * (-1)) // 翻转Y轴\n }\n break\n case 'Z': // Close path\n if (currentX !== startX || currentY !== startY) {\n coords.push(startX * scale, startY * scale * (-1)) // 翻转Y轴\n }\n break\n }\n })\n \n return coords\n }\n \n // 处理 SVG 树\n // 钻孔文件:plotResult.children 已包含每个孔的 imageShape.circle,且孔径来自显式小数,可靠;\n // 同时处理 svgTree 容易重复生成一套孔位,增加三角形数且可能引入坐标差异。\n if (!isDrillFile && svgTree && svgTree.children) {\n svgTree.children.forEach(child => processSvgElement(child))\n console.log(`[web-gerber] 从SVG树提取: ${vertices.length / 3} 顶点, ${indices.length / 3} 三角形`)\n }\n \n // 同时处理 plotResult.children(包含填充区域等SVG树可能遗漏的数据)\n if (plotResult.children && plotResult.children.length > 0) {\n const verticesBefore = vertices.length / 3\n console.log(`[web-gerber] 补充处理 plotResult.children (${plotResult.children.length} 个)`)\n \n // 直接从 plotResult.children 中提取几何数据\n // 统计各种类型的数量\n const typeCount = {}\n plotResult.children.forEach(child => {\n typeCount[child.type] = (typeCount[child.type] || 0) + 1\n })\n // console.log(`[web-gerber] 类型统计:`, typeCount)\n \n // 注意:这里不要重排 children 的顺序。\n // 因为 Gerber 的 LPC/LPD(clear/dark)是“有状态”的顺序布尔操作,\n // 若重排会导致清除/回填顺序被打乱,最终形状会缺失/错误。\n plotResult.children.forEach((child, idx) => {\n // 先根据 child.polarity 切换 polarity run(只影响后续写入的 indices 区间)\n ensurePolarity(child.polarity || 'dark')\n\n // scale 已经在外层定义了,这里直接使用\n \n // 处理 imagePath(线条和圆弧路径 - 普通Gerber文件)\n if (child.type === 'imagePath' && child.segments && child.segments.length > 0) {\n \n // 将路径离散化为线段 + 圆形端点\n child.segments.forEach((segment, segIdx) => {\n if (segment.type === 'line' && segment.start && segment.end) {\n const startX = segment.start[0] * scale\n const startY = segment.start[1] * scale\n const endX = segment.end[0] * scale\n const endY = segment.end[1] * scale\n \n const baseIndex = vertices.length / 3\n vertices.push(startX, startY, 0)\n vertices.push(endX, endY, 0)\n \n // 用三角形模拟线条:按 segment/child 真实线宽,缺省 0.15mm\n let width = ((segment.width ?? child.width ?? 0.15) * scale)\n \n // 应用最小线宽 (GKO层)\n if (width < minWidth) width = minWidth\n \n const radius = width / 2\n\n const dx = endX - startX\n const dy = endY - startY\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len > 0) {\n const nx = -dy / len * radius\n const ny = dx / len * radius\n \n // 线段主体(矩形)\n const v1 = vertices.length / 3\n vertices.push(startX + nx, startY + ny, 0)\n vertices.push(startX - nx, startY - ny, 0)\n vertices.push(endX + nx, endY + ny, 0)\n vertices.push(endX - nx, endY - ny, 0)\n \n indices.push(v1, v1 + 1, v1 + 2)\n indices.push(v1 + 1, v1 + 3, v1 + 2)\n \n // 在起点添加圆形端点(只在第一个线段添加)\n if (segIdx === 0) {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(startX, startY, 0)\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(startX + Math.cos(angle) * radius, startY + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 在终点添加圆形端点(每个线段的终点)\n {\n const segments = Math.max(8, Math.ceil(radius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(endX, endY, 0)\n \n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(endX + Math.cos(angle) * radius, endY + Math.sin(angle) * radius, 0)\n }\n \n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n } else if (segment.type === 'arc') {\n // 圆弧离散化(优先使用 web-gerber 提供的 theta,避免 0/2π 信息丢失)\n const sx = segment.start[0]\n const sy = segment.start[1]\n const ex = segment.end[0]\n const ey = segment.end[1]\n const cx0 = segment.center[0]\n const cy0 = segment.center[1]\n\n const startX = sx * scale\n const startY = sy * scale\n const endX = ex * scale\n const endY = ey * scale\n const cx = cx0 * scale\n const cy = cy0 * scale\n \n const startAngle = (segment.start.length > 2 && typeof segment.start[2] === 'number')\n ? segment.start[2]\n : Math.atan2(sy - cy0, sx - cx0)\n const endAngle = (segment.end.length > 2 && typeof segment.end[2] === 'number')\n ? segment.end[2]\n : Math.atan2(ey - cy0, ex - cx0)\n \n const arcRadius = ((typeof segment.radius === 'number' && segment.radius > 0)\n ? segment.radius\n : Math.sqrt(Math.pow(sx - cx0, 2) + Math.pow(sy - cy0, 2))) * scale\n \n // 检测是否为完整的圆(起点和终点相同)\n const isFullCircle = Math.abs(startX - endX) < 0.001 && Math.abs(startY - endY) < 0.001\n let angleDiff = endAngle - startAngle\n \n if (isFullCircle) {\n // 完整的圆,画360度\n angleDiff = Math.PI * 2\n if (idx < 5) {\n // console.log(`[web-gerber] 检测到完整圆弧: 圆心(${cx.toFixed(2)}, ${cy.toFixed(2)}), 半径=${arcRadius.toFixed(2)}mm`)\n }\n } else if (Math.abs(angleDiff) < 0.001) {\n // 角度差太小,跳过\n return\n }\n \n const arcSegments = Math.max(8, Math.ceil(Math.abs(angleDiff) * arcRadius / 0.5))\n // 圆弧线宽:按 segment/child 真实线宽,缺省 0.15mm\n let arcWidth = ((segment.width ?? child.width ?? 0.15) * scale)\n \n // 应用最小线宽 (GKO层)\n if (arcWidth < minWidth) arcWidth = minWidth\n \n const lineRadius = arcWidth / 2 // 线宽的半径\n\n for (let i = 0; i < arcSegments; i++) {\n const t1 = i / arcSegments\n const t2 = (i + 1) / arcSegments\n const angle1 = startAngle + angleDiff * t1\n const angle2 = startAngle + angleDiff * t2\n \n const x1 = cx + Math.cos(angle1) * arcRadius\n const y1 = cy + Math.sin(angle1) * arcRadius\n const x2 = cx + Math.cos(angle2) * arcRadius\n const y2 = cy + Math.sin(angle2) * arcRadius\n \n // 为圆弧添加线宽(用三角形模拟)\n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len > 0) {\n const nx = -dy / len * lineRadius\n const ny = dx / len * lineRadius\n \n const v1 = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(v1, v1 + 1, v1 + 2)\n indices.push(v1 + 1, v1 + 3, v1 + 2)\n }\n }\n \n // 在圆弧起点添加圆形端点(只在第一个segment或非完整圆时添加)\n if (!isFullCircle && segIdx === 0) {\n const capSegments = Math.max(8, Math.ceil(lineRadius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(startX, startY, 0)\n \n for (let j = 0; j <= capSegments; j++) {\n const angle = (j / capSegments) * Math.PI * 2\n vertices.push(startX + Math.cos(angle) * lineRadius, startY + Math.sin(angle) * lineRadius, 0)\n }\n \n for (let j = 0; j < capSegments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 在圆弧终点添加圆形端点(非完整圆时添加)\n if (!isFullCircle) {\n const capSegments = Math.max(8, Math.ceil(lineRadius * 2))\n const centerIdx = vertices.length / 3\n vertices.push(endX, endY, 0)\n \n for (let j = 0; j <= capSegments; j++) {\n const angle = (j / capSegments) * Math.PI * 2\n vertices.push(endX + Math.cos(angle) * lineRadius, endY + Math.sin(angle) * lineRadius, 0)\n }\n \n for (let j = 0; j < capSegments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n })\n }\n // 处理 imageShape(圆形和其他形状)\n else if (child.type === 'imageShape' && child.shape) {\n const shape = child.shape\n const polarity = child.polarity || 'dark' // 默认为 dark\n \n // if (idx < 10) {\n // console.log(`[web-gerber] Shape ${idx}:`, shape, `polarity: ${polarity}`)\n // }\n \n // web-gerber 的 shape.type 主要是:\n // - circle / rectangle / polygon / outline / layeredShape\n // 之前这里只处理了 circle + (误写的 rect),会导致部分 pad/宏形状缺失。\n\n const emitCircle = (cx, cy, r) => {\n if (!(r > 0)) return\n // 位置坐标应用 posScale(包含 FILE_FORMAT 坐标缩放修正);孔径来自显式小数,不应被放大\n const x = cx * posScale\n const y = cy * posScale\n const radius = r * sizeScale\n \n const segments = Math.min(Math.max(Math.ceil(2 * Math.PI * radius / 0.2), 12), 64)\n const baseIndex = vertices.length / 3\n \n // 中心点\n vertices.push(x, y, 0)\n // 圆周点\n for (let i = 0; i <= segments; i++) {\n const angle = (i / segments) * Math.PI * 2\n vertices.push(x + Math.cos(angle) * radius, y + Math.sin(angle) * radius, 0)\n }\n for (let i = 0; i < segments; i++) {\n indices.push(baseIndex, baseIndex + 1 + i, baseIndex + 1 + i + 1)\n }\n \n // 额外添加10个中心点(用于特征提取)\n for (let i = 0; i < 10; i++) {\n vertices.push(x, y, 0)\n }\n\n // 记录本次 circle 的顶点区间(用于后续只缩放孔位)\n if (drillPosScaleRuns) {\n const endIndex = vertices.length / 3\n drillPosScaleRuns.push({ start: baseIndex, count: endIndex - baseIndex, cx: x, cy: y, kind: 'circle' })\n }\n }\n\n const emitRectangle = (x, y, xSize, ySize, r) => {\n const x0 = (x || 0) * posScale\n const y0 = (y || 0) * posScale\n const w = (xSize || 0) * sizeScale\n const h = (ySize || 0) * sizeScale\n if (!(w > 0 && h > 0)) return\n\n // r 是可选圆角半径(单位同 plot units),这里先按直角矩形渲染,避免“缺失”\n const baseIndex = vertices.length / 3\n vertices.push(x0, y0, 0)\n vertices.push(x0 + w, y0, 0)\n vertices.push(x0 + w, y0 + h, 0)\n vertices.push(x0, y0 + h, 0)\n indices.push(baseIndex, baseIndex + 1, baseIndex + 2)\n indices.push(baseIndex, baseIndex + 2, baseIndex + 3)\n\n // 额外添加中心点(用于特征提取)\n // 很多通孔焊盘/阻焊开窗是“长圆形/圆角矩形”(obround),如果只提取 circle 圆心会导致参考层特征不足,\n // 从而在钻孔只有“一条直线分布”时匹配不稳定。\n const cx = x0 + w / 2\n const cy = y0 + h / 2\n for (let i = 0; i < 10; i++) {\n vertices.push(cx, cy, 0)\n }\n\n // 记录本次 rectangle 的顶点区间(用于后续只缩放孔位)\n if (drillPosScaleRuns) {\n const endIndex = vertices.length / 3\n drillPosScaleRuns.push({ start: baseIndex, count: endIndex - baseIndex, cx, cy, kind: 'rectangle' })\n }\n }\n\n const emitPolygon = (pts) => {\n if (!Array.isArray(pts) || pts.length < 3) return\n const flat = []\n for (const p of pts) {\n if (!p || p.length < 2) continue\n flat.push(p[0] * posScale, p[1] * posScale)\n }\n if (flat.length < 6) return\n\n const triangles = earcut(flat)\n if (!triangles || triangles.length === 0) return\n const baseIndex = vertices.length / 3\n for (let i = 0; i < flat.length; i += 2) {\n vertices.push(flat[i], flat[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n\n const emitOutline = (segments) => {\n if (!Array.isArray(segments) || segments.length === 0) return\n const pts = []\n let started = false\n\n const pushPoint = (x, y) => {\n pts.push(x * posScale, y * posScale)\n }\n\n for (const seg of segments) {\n if (!seg || !seg.type) continue\n\n if (seg.type === 'line' && seg.start && seg.end) {\n if (!started) {\n pushPoint(seg.start[0], seg.start[1])\n started = true\n }\n pushPoint(seg.end[0], seg.end[1])\n } else if (seg.type === 'arc' && seg.start && seg.end && seg.center) {\n // ArcPosition: [x,y,theta],theta 为弧度\n const cx = seg.center[0] * posScale\n const cy = seg.center[1] * posScale\n const radius = (seg.radius ?? Math.sqrt(Math.pow(seg.start[0] - seg.center[0], 2) + Math.pow(seg.start[1] - seg.center[1], 2))) * posScale\n\n const startTheta = (seg.start.length > 2 ? seg.start[2] : Math.atan2(seg.start[1] - seg.center[1], seg.start[0] - seg.center[0]))\n let endTheta = (seg.end.length > 2 ? seg.end[2] : Math.atan2(seg.end[1] - seg.center[1], seg.end[0] - seg.center[0]))\n // 假定 arc 按 CCW,从 startTheta 走到 endTheta(跨 0 则 +2π)\n if (endTheta < startTheta) endTheta += Math.PI * 2\n\n if (!started) {\n pushPoint(seg.start[0], seg.start[1])\n started = true\n }\n\n const arcLen = Math.abs(endTheta - startTheta) * radius\n const arcSegments = Math.max(8, Math.ceil(arcLen / 0.5))\n for (let i = 1; i <= arcSegments; i++) {\n const t = i / arcSegments\n const theta = startTheta + (endTheta - startTheta) * t\n const x = cx + Math.cos(theta) * radius\n const y = cy + Math.sin(theta) * radius\n pts.push(x, y)\n }\n }\n }\n\n if (pts.length < 6) return\n const triangles = earcut(pts)\n if (!triangles || triangles.length === 0) return\n const baseIndex = vertices.length / 3\n for (let i = 0; i < pts.length; i += 2) {\n vertices.push(pts[i], pts[i + 1], 0)\n }\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n }\n\n const emitSimpleShape = (s) => {\n if (!s || !s.type) return\n if (s.type === 'circle') {\n emitCircle(s.cx || 0, s.cy || 0, s.r || 0)\n } else if (s.type === 'rectangle') {\n emitRectangle(s.x || 0, s.y || 0, s.xSize || 0, s.ySize || 0, s.r || 0)\n } else if (s.type === 'polygon') {\n emitPolygon(s.points || [])\n } else if (s.type === 'outline') {\n emitOutline(s.segments || [])\n }\n }\n\n // layeredShape(宏)可能包含 erase 子形状(需要在同一图层内“挖空”)\n if (shape.type === 'layeredShape' && Array.isArray(shape.shapes)) {\n for (const sub of shape.shapes) {\n const localPolarity = sub && sub.erase === true ? 'clear' : polarity\n ensurePolarity(localPolarity)\n emitSimpleShape(sub)\n }\n ensurePolarity(polarity)\n } else {\n // 普通 shape:沿用 child 的 polarity\n emitSimpleShape(shape)\n }\n }\n // 处理 imageRegion(区域填充 - G36/G37)\n else if (child.type === 'imageRegion' && child.segments && child.segments.length > 0) {\n // 打印所有 Region 的基本信息(已禁用以减少日志量)\n // console.log(`[web-gerber] Region ${idx}: segments=${child.segments.length}, 类型=${child.segments.map(s => s.type).join(',')}`)\n \n // ⚠️ 不在此处过滤任何内容,确保 Gerber图/骨架线框模式显示完整\n // 字符孔洞的处理交给仿真模式专用的 triangulateSilkscreenFromSvg 流程\n \n // 提取区域轮廓点(用于三角化填充)\n const regionPoints = []\n const holeIndices = []\n let lastEnd = null\n \n child.segments.forEach((segment, segIdx) => {\n let startX, startY, endX, endY\n let isArc = false\n let arcParams = null\n \n if (segment.type === 'line') {\n startX = segment.start[0] * scale\n startY = segment.start[1] * scale\n endX = segment.end[0] * scale\n endY = segment.end[1] * scale\n } else if (segment.type === 'arc') {\n isArc = true\n // web-gerber 的 arc.start/end 是 ArcPosition: [x, y, theta],theta 很关键:\n // 例如点在右侧时,theta 可能是 0 或 2π,用 atan2 计算会丢失 2π 的信息,\n // 从而把“下半圆”错误地走成“上半圆”(SlotHoles 会缺一个圆帽)。\n const sx = segment.start[0]\n const sy = segment.start[1]\n const ex = segment.end[0]\n const ey = segment.end[1]\n\n startX = sx * scale\n startY = sy * scale\n endX = ex * scale\n endY = ey * scale\n\n const cx0 = segment.center[0]\n const cy0 = segment.center[1]\n const cx = cx0 * scale\n const cy = cy0 * scale\n\n const startAngle = (segment.start.length > 2 && typeof segment.start[2] === 'number')\n ? segment.start[2]\n : Math.atan2(sy - cy0, sx - cx0)\n const endAngle = (segment.end.length > 2 && typeof segment.end[2] === 'number')\n ? segment.end[2]\n : Math.atan2(ey - cy0, ex - cx0)\n\n const radius0 = (typeof segment.radius === 'number' && segment.radius > 0)\n ? segment.radius\n : Math.sqrt(Math.pow(sx - cx0, 2) + Math.pow(sy - cy0, 2))\n const radius = radius0 * scale\n\n // 按 web-gerber 提供的 theta 方向走(不做“最短弧”归一化)\n let angleDiff = endAngle - startAngle\n\n // 检测是否为完整的圆(起点和终点相同且角度差接近0)\n const isSamePoint = Math.abs(sx - ex) < 1e-9 && Math.abs(sy - ey) < 1e-9\n if (isSamePoint && Math.abs(angleDiff) < 1e-6) {\n angleDiff = Math.PI * 2\n }\n \n arcParams = { cx, cy, radius, startAngle, angleDiff }\n }\n \n // 检查断点(新轮廓)\n let isNewContour = false\n if (lastEnd) {\n const dist = Math.sqrt(Math.pow(startX - lastEnd.x, 2) + Math.pow(startY - lastEnd.y, 2))\n if (dist > 0.01) { // 0.01mm 容差\n isNewContour = true\n holeIndices.push(regionPoints.length / 2)\n }\n } else {\n isNewContour = true // 第一个轮廓\n }\n \n // 如果是新轮廓,添加起点\n if (isNewContour) {\n regionPoints.push(startX, startY)\n }\n \n if (isArc && arcParams) {\n // 离散化圆弧\n const { cx, cy, radius, startAngle, angleDiff } = arcParams\n const arcLength = Math.abs(angleDiff) * radius\n const arcSegments = Math.max(8, Math.ceil(arcLength / 0.5))\n \n for (let i = 1; i <= arcSegments; i++) {\n const t = i / arcSegments\n const angle = startAngle + angleDiff * t\n regionPoints.push(\n cx + Math.cos(angle) * radius,\n cy + Math.sin(angle) * radius\n )\n }\n } else {\n // 线段:添加终点\n regionPoints.push(endX, endY)\n }\n \n lastEnd = { x: endX, y: endY }\n })\n \n // 使用 earcut 三角化填充区域\n if (regionPoints.length >= 6) { // 至少3个点\n try {\n const triangles = earcut(regionPoints, holeIndices)\n \n if (triangles.length > 0) {\n const baseIndex = vertices.length / 3\n \n // 添加顶点\n for (let i = 0; i < regionPoints.length; i += 2) {\n vertices.push(regionPoints[i], regionPoints[i + 1], 0)\n }\n \n // 添加三角形索引\n for (let i = 0; i < triangles.length; i++) {\n indices.push(baseIndex + triangles[i])\n }\n \n // 对于轮廓层,额外绘制描边(加粗轮廓线)\n if (isOutlineLayer && minWidth > 0) {\n const strokeRadius = minWidth / 2\n \n // 绘制轮廓边框(连接每个点)\n for (let i = 0; i < regionPoints.length - 2; i += 2) {\n const x1 = regionPoints[i]\n const y1 = regionPoints[i + 1]\n const x2 = regionPoints[i + 2]\n const y2 = regionPoints[i + 3]\n \n const dx = x2 - x1\n const dy = y2 - y1\n const len = Math.sqrt(dx * dx + dy * dy)\n \n if (len > 0.001) {\n const nx = -dy / len * strokeRadius\n const ny = dx / len * strokeRadius\n \n // 线段主体(矩形)\n const v1 = vertices.length / 3\n vertices.push(x1 + nx, y1 + ny, 0)\n vertices.push(x1 - nx, y1 - ny, 0)\n vertices.push(x2 + nx, y2 + ny, 0)\n vertices.push(x2 - nx, y2 - ny, 0)\n \n indices.push(v1, v1 + 1, v1 + 2)\n indices.push(v1 + 1, v1 + 3, v1 + 2)\n \n // 圆形端点\n const segments = Math.max(8, Math.ceil(strokeRadius * 4))\n \n // 起点圆\n if (i === 0) {\n const centerIdx = vertices.length / 3\n vertices.push(x1, y1, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x1 + Math.cos(angle) * strokeRadius, y1 + Math.sin(angle) * strokeRadius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n \n // 终点圆\n const centerIdx = vertices.length / 3\n vertices.push(x2, y2, 0)\n for (let j = 0; j <= segments; j++) {\n const angle = (j / segments) * Math.PI * 2\n vertices.push(x2 + Math.cos(angle) * strokeRadius, y2 + Math.sin(angle) * strokeRadius, 0)\n }\n for (let j = 0; j < segments; j++) {\n indices.push(centerIdx, centerIdx + 1 + j, centerIdx + 1 + j + 1)\n }\n }\n }\n }\n }\n } catch (error) {\n console.error(`[web-gerber] Region ${idx} 三角化失败:`, error)\n }\n }\n }\n })\n\n // 结束最后一个 polarity run(只要本次解析产生了 indices)\n if (indices.length > currentRunStart) {\n polarityRuns.push({ polarity: currentPolarity, start: currentRunStart, end: indices.length })\n }\n \n const verticesAdded = (vertices.length / 3) - verticesBefore\n const trianglesAdded = (indices.length / 3) - (verticesBefore > 0 ? indices.length / 3 - verticesBefore : 0)\n console.log(`[web-gerber] 补充提取: +${verticesAdded} 顶点, 总计 ${vertices.length / 3} 顶点, ${indices.length / 3} 三角形`)\n } // plotResult.children 处理结束\n \n // 最终统计\n console.log(`[web-gerber] ${fileName}: ${vertices.length / 3} 顶点, ${indices.length / 3} 三角形`)\n \n // 计算并输出坐标范围\n if (vertices.length > 0) {\n let minX = Infinity, maxX = -Infinity\n let minY = Infinity, maxY = -Infinity\n for (let i = 0; i < vertices.length; i += 3) {\n minX = Math.min(minX, vertices[i])\n maxX = Math.max(maxX, vertices[i])\n minY = Math.min(minY, vertices[i + 1])\n maxY = Math.max(maxY, vertices[i + 1])\n }\n console.log(`[web-gerber] ${fileName} 坐标范围: X=[${minX.toFixed(2)}, ${maxX.toFixed(2)}] mm, Y=[${minY.toFixed(2)}, ${maxY.toFixed(2)}] mm`)\n }\n \n return {\n vertices: new Float32Array(vertices),\n triangleIndices: new Uint32Array(indices),\n polarityRuns: polarityRuns.filter(r => (r.end - r.start) > 0),\n // 仅 drill 文件会有内容\n drillPosScaleRuns: drillPosScaleRuns && drillPosScaleRuns.length > 0 ? drillPosScaleRuns : null\n }\n } catch (error) {\n console.error(`[web-gerber] 解析失败:`, error)\n console.error(`[web-gerber] 错误堆栈:`, error.stack)\n throw error\n }\n}\n\n/**\n * 获取图层的显示名称\n * @param {string|Object} fileNameOrLayer - 文件名或图层对象\n */\nfunction getLayerDisplayName(fileNameOrLayer) {\n // 如果是图层对象,检查是否有存储的 phoLayerType\n if (typeof fileNameOrLayer === 'object' && fileNameOrLayer !== null) {\n if (fileNameOrLayer.phoLayerType) {\n return fileNameOrLayer.phoLayerType\n }\n fileNameOrLayer = fileNameOrLayer.name\n }\n \n const fileName = fileNameOrLayer\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // .gbr 文件:优先检查,根据文件名模式识别图层类型(必须在通用后缀匹配之前)\n if (fileExtension === '.GBR') {\n const nameUpper = fileNameOnly.toUpperCase()\n const nameLower = fileNameOnly.toLowerCase()\n\n // ✅ 关键补充:如果 basename 本身就是标准层名(例如 GTS.gbr / GTL.gbr / GKO.gbr),直接按层名识别\n const baseNameUpper = fileNameOnly.replace(/\\.[^.]+$/, '').toUpperCase()\n const directLayerNames = new Set([\n 'GTO', 'GTP', 'GTS', 'GTL',\n 'GBL', 'GBS', 'GBP', 'GBO',\n 'GKO',\n ])\n if (directLayerNames.has(baseNameUpper) || /^G\\d+$/i.test(baseNameUpper) || /^SIG\\d+$/i.test(baseNameUpper)) {\n return baseNameUpper\n }\n \n // legend_Top / Legend_Top -> GTO (优先匹配,使用更精确的匹配)\n if (nameLower.includes('legend_top') || \n (nameLower.includes('legend') && nameLower.includes('top') && !nameLower.includes('signal'))) {\n return 'GTO'\n }\n \n // Paste_Top -> GTP\n if (nameLower.includes('paste_top')) {\n return 'GTP'\n }\n \n // Paste_Bot -> GBP\n if (nameLower.includes('paste_bot')) {\n return 'GBP'\n }\n \n // soldermask_Top / Soldermask_Top -> GTS\n if (nameLower.includes('soldermask_top')) {\n return 'GTS'\n }\n \n // soldermask_Bot / Soldermask_Bot -> GBS\n if (nameLower.includes('soldermask_bot')) {\n return 'GBS'\n }\n \n // signal_1 -> SIG2, signal_2 -> SIG3, 依次类推 (包括 Copper_Signal_1, Copper_Signal_2)\n // 注意:这个匹配必须在 signal_Top/Bot 之前,避免误匹配\n const signalMatch = nameLower.match(/(?:copper_)?signal[_\\s](\\d+)/i)\n if (signalMatch) {\n const number = parseInt(signalMatch[1], 10)\n const displayNumber = number + 1\n return 'SIG' + displayNumber\n }\n \n // signal_Top -> GTL (包括 Copper_Signal_Top)\n if (nameLower.includes('signal_top') || nameLower.includes('copper_signal_top')) {\n return 'GTL'\n }\n \n // signal_Bot -> GBL (包括 Copper_Signal_Bot)\n if (nameLower.includes('signal_bot') || nameLower.includes('copper_signal_bot')) {\n return 'GBL'\n }\n \n // Profile -> GKO\n if (nameUpper.includes('PROFILE')) {\n return 'GKO'\n }\n\n // KiCad naming: supports underscore (v6+), dot (v5), and hyphen separators\n // e.g. project-F_Cu.gbr, led_blinker-F.Cu.gbr, board-Edge-Cuts.gbr\n if (/EDGE[._-]CUTS/i.test(nameUpper)) return 'GKO'\n if (/F[._-]CU\\b/i.test(nameUpper)) return 'GTL'\n if (/B[._-]CU\\b/i.test(nameUpper)) return 'GBL'\n if (/F[._-]MASK/i.test(nameUpper)) return 'GTS'\n if (/B[._-]MASK/i.test(nameUpper)) return 'GBS'\n if (/F[._-]SILKS?/i.test(nameUpper)) return 'GTO'\n if (/B[._-]SILKS?/i.test(nameUpper)) return 'GBO'\n if (/F[._-]PASTE/i.test(nameUpper)) return 'GTP'\n if (/B[._-]PASTE/i.test(nameUpper)) return 'GBP'\n if (/F[._-]FAB/i.test(nameUpper)) return 'GTO'\n if (/B[._-]FAB/i.test(nameUpper)) return 'GBO'\n // KiCad InN_Cu:第 N 个内层铜(与 signal_1→SIG2 一致:物理层号 = N+1,顶铜为第 1 层)\n if (/IN(\\d+)[._-]CU/i.test(nameUpper)) {\n const m = nameUpper.match(/IN(\\d+)[._-]CU/)\n return 'SIG' + (parseInt(m[1], 10) + 1)\n }\n\n // ZH-TOP -> GTS (保留兼容性)\n if (nameUpper.startsWith('ZH-TOP') || nameUpper.includes('ZH-TOP')) return 'GTS'\n // ZH-BOT -> GBS (保留兼容性)\n if (nameUpper.startsWith('ZH-BOT') || nameUpper.includes('ZH-BOT')) return 'GBS'\n // XL-TOP -> GTL (保留兼容性)\n if (nameUpper.startsWith('XL-TOP') || nameUpper.includes('XL-TOP')) return 'GTL'\n // XL-BOT -> GBL (保留兼容性)\n if (nameUpper.startsWith('XL-BOT') || nameUpper.includes('XL-BOT')) return 'GBL'\n\n // 检查是否包含特定关键词,如果包含则显示关键词部分\n const keywords = ['_Drawing', '_Drillmap', '_NPTH', '_Pads', '_PTH']\n for (const keyword of keywords) {\n if (fileNameOnly.includes(keyword)) {\n // 提取关键词及其后面的部分(包含扩展名)\n const keywordIndex = fileNameOnly.indexOf(keyword)\n return fileNameOnly.substring(keywordIndex)\n }\n }\n // 其他 .gbr 文件显示完整文件名(包含扩展名)\n return fileNameOnly\n }\n \n // 检查文件名后缀模式\n if (fileNameUpper.endsWith('_SST') || fileNameUpper.includes('_SST.')) return 'GTO'\n if (fileNameUpper.endsWith('_PMT') || fileNameUpper.includes('_PMT.')) return 'GTP'\n if (fileNameUpper.endsWith('_SMT') || fileNameUpper.includes('_SMT.')) return 'GTS'\n if (fileNameUpper.endsWith('_TOP') || fileNameUpper.includes('_TOP.')) return 'GTL'\n if (fileNameUpper.endsWith('_SMB') || fileNameUpper.includes('_SMB.')) return 'GBS'\n if (fileNameUpper.endsWith('_PMB') || fileNameUpper.includes('_PMB.')) return 'GBP'\n if (fileNameUpper.endsWith('_SSB') || fileNameUpper.includes('_SSB.')) return 'GBO'\n if (fileNameUpper.includes('OUTLINE')) return 'GKO'\n if (/.*_INT\\d+$/i.test(fileNameUpper) || /.*_INT\\d+\\./i.test(fileNameUpper)) return fileNameOnly\n \n // 检查扩展名\n if (fileExtension === '.D' || fileExtension === '.DRL' || fileExtension === '.DRI') {\n // 特殊命名:np.drl(非金属化孔)与 p.drl(金属化孔)\n if (fileNameUpper === 'NP.DRL' || fileNameUpper === 'NP.D') return 'DRL2'\n if (fileNameUpper === 'P.DRL' || fileNameUpper === 'P.D') return 'DRL'\n return 'DRL'\n }\n if (fileExtension === '.ROU') return 'rout'\n \n // CAM350 等软件导出的扩展名映射\n if (fileExtension === '.BOT') return 'GBL' // 底层铜\n if (fileExtension === '.TOP') return 'GTL' // 顶层铜\n if (fileExtension === '.SOB') return 'GBS' // 底层阻焊 (Solder mask Bottom)\n if (fileExtension === '.SOT') return 'GTS' // 顶层阻焊 (Solder mask Top)\n if (fileExtension === '.SST') return 'GTO' // 顶层丝印 (Silkscreen Top)\n if (fileExtension === '.SSB') return 'GBO' // 底层丝印 (Silkscreen Bottom)\n if (fileExtension === '.SMT') return 'GTS' // 顶层阻焊 (Solder Mask Top)\n if (fileExtension === '.SMB') return 'GBS' // 底层阻焊 (Solder Mask Bottom)\n if (fileExtension === '.PMT') return 'GTP' // 顶层锡膏 (Paste Mask Top)\n if (fileExtension === '.PMB') return 'GBP' // 底层锡膏 (Paste Mask Bottom)\n if (fileExtension === '.SER') return fileNameOnly // 丝印层,直接显示文件名\n if (fileExtension === '.ART') {\n // .art 文件:按文件名(不含扩展名)映射为标准层类型\n // SST -> GTO, SMT -> GTS, Top -> GTL, Bot -> GBL, SMB -> GBS, SSB -> GBO, OUT -> GKO\n const dot = fileNameOnly.lastIndexOf('.')\n const stemUpper = (dot > 0 ? fileNameOnly.substring(0, dot) : fileNameOnly).toUpperCase()\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 // 其他 .art:直接展示文件名+扩展名\n return fileNameOnly\n }\n \n // .pho 文件:根据文件名前缀和数字大小识别图层类型\n if (fileExtension === '.PHO') {\n const nameUpper = fileNameOnly.toUpperCase()\n \n // sst 开头 → GTO\n if (nameUpper.startsWith('SST')) {\n return 'GTO'\n }\n \n // smd 开头,需要判断数字大小\n if (nameUpper.startsWith('SMD')) {\n const numberMatch = nameUpper.match(/^SMD(\\d+)/)\n if (numberMatch) {\n const number = parseInt(numberMatch[1], 10)\n // 数字小(<20)→ GTP,数字大(>=20)→ GBP\n return number < 20 ? 'GTP' : 'GBP'\n } else {\n // 没有数字,默认按 GTP 处理\n return 'GTP'\n }\n }\n \n // sm 开头,需要判断数字大小\n if (nameUpper.startsWith('SM')) {\n const numberMatch = nameUpper.match(/^SM(\\d+)/)\n if (numberMatch) {\n const number = parseInt(numberMatch[1], 10)\n // 数字小(<20)→ GTS,数字大(>=20)→ GBS\n return number < 20 ? 'GTS' : 'GBS'\n } else {\n // 没有数字,默认按 GTS 处理\n return 'GTS'\n }\n }\n \n // art 开头,需要判断数字大小\n if (nameUpper.startsWith('ART')) {\n const numberMatch = nameUpper.match(/^ART(\\d+)/)\n if (numberMatch) {\n const number = parseInt(numberMatch[1], 10)\n // 数字小(<20)→ GTL,数字大(>=20)→ GBL\n return number < 20 ? 'GTL' : 'GBL'\n } else {\n // 没有数字,默认按 GTL 处理\n return 'GTL'\n }\n }\n \n // ssb 开头 → GBO\n if (nameUpper.startsWith('SSB')) {\n return 'GBO'\n }\n \n // drl 开头 → DRL\n if (nameUpper.startsWith('DRL')) {\n return 'DRL'\n }\n \n // dd 开头 → 显示完整文件名\n if (nameUpper.startsWith('DD')) {\n return fileNameOnly\n }\n \n // 其他 .pho 文件显示完整文件名\n return fileNameOnly\n }\n \n // GD+数字、GG+数字 显示完整文件名(包含扩展名)\n if (/^\\.GD\\d+$/i.test(fileExtension) || /^\\.GG\\d+$/i.test(fileExtension)) {\n return fileNameOnly\n }\n \n // GM 和 GM+数字(包括 GM1)显示完整文件名(包含扩展名)\n if (fileExtension === '.GM' || /^\\.GM\\d+$/i.test(fileExtension)) {\n return fileNameOnly\n }\n \n // INT+数字 显示完整文件名(包含扩展名)\n if (/^\\.INT\\d+$/i.test(fileExtension)) {\n return fileNameOnly\n }\n \n // G+数字 (.g2, .g3 ...) — KiCad/Protel 内层铜箔\n const gNumberMatch = fileExtension.match(/^\\.G(\\d+)$/i)\n if (gNumberMatch) {\n // KiCad: 文件名含 InN_Cu,显示名与内层序号一致(SIG(N+1)),不采用扩展名上的 .g2/.g3 编号\n const inCuMatch = fileNameUpper.match(/IN(\\d+)[._-]CU/i)\n if (inCuMatch) return 'SIG' + (parseInt(inCuMatch[1], 10) + 1)\n // Protel/Altium 惯例:.g1 = 第 1 内层 → SIG2\n const number = parseInt(gNumberMatch[1], 10)\n return 'SIG' + (number + 1)\n }\n \n // 如果是 GP+数字 格式,显示为 GP+数字\n const gpNumberMatch = fileExtension.match(/^\\.GP(\\d+)$/i)\n if (gpNumberMatch) {\n const number = parseInt(gpNumberMatch[1], 10)\n return 'GP' + number\n }\n \n // TXT 和 TX+数字 文件\n const isTxFile = /^\\.TX\\d*$/i.test(fileExtension) || fileExtension === '.TXT'\n if (isTxFile) {\n // 检查文件名中是否包含特定关键词\n if (/slotholes/i.test(fileName)) {\n return 'SLOT'\n }\n if (/[_\\s]via/i.test(fileName)) {\n return 'VIA'\n }\n // M48 格式的钻孔文件显示为 DRL\n // 注意:这里无法直接读取文件内容判断,但在图层列表中会通过 layer.isDrillFile 标记\n // 暂时显示扩展名,如果需要可以在 updateLayerList 中根据 layer.isDrillFile 动态修改\n return 'DRL'\n }\n \n // 默认返回扩展名(去掉点号)\n return fileExtension.substring(1)\n}\n\n/**\n * 获取文件的排序值\n */\nfunction getLayerOrder(fileName) {\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n const ext = fileExtension.substring(1) // 去掉点号\n \n const layerTypeOrder = {\n 'GTO': 0, 'GTP': 1, 'GTS': 2, 'GTL': 3, 'G1': 4, 'G2': 5,\n 'GBL': 6, 'GBS': 7, 'GBP': 7.5, 'GBO': 8, 'GKO': 9,\n 'SLOT': 9.5, 'DRL_TXT': 9.6, 'VIA': 11, 'rout': 11.5,\n 'GDD': 12, 'GDL': 13, 'GM': 14, 'GPB': 14.5, 'GPT': 15,\n 'DRL': 200,\n }\n \n // .gbr 文件:优先检查,根据文件名模式判断图层类型(必须在通用后缀匹配之前)\n if (ext === 'GBR') {\n const nameUpper = fileNameOnly.toUpperCase()\n const nameLower = fileNameOnly.toLowerCase()\n\n // ✅ 关键补充:如果 basename 本身就是标准层名(例如 GTS.gbr),直接使用对应 order\n const baseNameUpper = fileNameOnly.replace(/\\.[^.]+$/, '').toUpperCase()\n if (layerTypeOrder[baseNameUpper] !== undefined || /^G\\d+$/i.test(baseNameUpper) || /^SIG\\d+$/i.test(baseNameUpper)) {\n const order = layerTypeOrder[baseNameUpper] !== undefined ? layerTypeOrder[baseNameUpper] : 10\n console.log(`文件 ${fileName} 识别为 GBR->${baseNameUpper} (basename), order=${order}`)\n return order\n }\n \n // legend_Top -> GTO (order=0) (优先匹配,使用更精确的匹配)\n if (nameLower.includes('legend_top') || \n (nameLower.includes('legend') && nameLower.includes('top') && !nameLower.includes('signal'))) {\n console.log(`文件 ${fileName} 识别为 GBR->GTO (legend_Top), order=0`)\n return 0\n }\n \n // Paste_Top -> GTP (order=1)\n if (nameLower.includes('paste_top')) {\n console.log(`文件 ${fileName} 识别为 GBR->GTP (Paste_Top), order=1`)\n return 1\n }\n \n // Paste_Bot -> GBP (order=7.5)\n if (nameLower.includes('paste_bot')) {\n console.log(`文件 ${fileName} 识别为 GBR->GBP (Paste_Bot), order=7.5`)\n return 7.5\n }\n \n // soldermask_Top -> GTS (order=2)\n if (nameLower.includes('soldermask_top')) {\n console.log(`文件 ${fileName} 识别为 GBR->GTS (soldermask_Top), order=2`)\n return 2\n }\n \n // soldermask_Bot -> GBS (order=7)\n if (nameLower.includes('soldermask_bot')) {\n console.log(`文件 ${fileName} 识别为 GBR->GBS (soldermask_Bot), order=7`)\n return 7\n }\n \n // signal_1 -> SIG2 (order=3.1), signal_2 -> SIG3 (order=3.2), 依次类推 (包括 Copper_Signal_1, Copper_Signal_2)\n // 注意:这个匹配必须在 signal_Top/Bot 之前,避免误匹配\n const signalMatch = nameLower.match(/(?:copper_)?signal[_\\s](\\d+)/i)\n if (signalMatch) {\n const number = parseInt(signalMatch[1], 10)\n const order = Math.min(3 + number * 0.1, 5.9)\n console.log(`文件 ${fileName} 识别为 GBR->SIG${number + 1} (signal_${number}), order=${order}`)\n return order\n }\n \n // signal_Top -> GTL (order=3) (包括 Copper_Signal_Top)\n if (nameLower.includes('signal_top') || nameLower.includes('copper_signal_top')) {\n console.log(`文件 ${fileName} 识别为 GBR->GTL (signal_Top), order=3`)\n return 3\n }\n \n // signal_Bot -> GBL (order=6) (包括 Copper_Signal_Bot)\n if (nameLower.includes('signal_bot') || nameLower.includes('copper_signal_bot')) {\n console.log(`文件 ${fileName} 识别为 GBR->GBL (signal_Bot), order=6`)\n return 6\n }\n \n // Profile -> GKO (order=9)\n if (nameUpper.includes('PROFILE')) {\n console.log(`文件 ${fileName} 识别为 GBR->GKO (Profile), order=9`)\n return 9\n }\n\n // KiCad naming (underscore / dot / hyphen separators)\n if (/EDGE[._-]CUTS/i.test(nameUpper)) return 9 // GKO\n if (/F[._-]CU\\b/i.test(nameUpper)) return 3 // GTL\n if (/B[._-]CU\\b/i.test(nameUpper)) return 6 // GBL\n if (/F[._-]MASK/i.test(nameUpper)) return 2 // GTS\n if (/B[._-]MASK/i.test(nameUpper)) return 7 // GBS\n if (/F[._-]SILKS?/i.test(nameUpper)) return 0 // GTO\n if (/B[._-]SILKS?/i.test(nameUpper)) return 8 // GBO\n if (/F[._-]PASTE/i.test(nameUpper)) return 1 // GTP\n if (/B[._-]PASTE/i.test(nameUpper)) return 7.5 // GBP\n if (/F[._-]FAB/i.test(nameUpper)) return 0 // GTO\n if (/B[._-]FAB/i.test(nameUpper)) return 8 // GBO\n if (/IN(\\d+)[._-]CU/i.test(nameUpper)) {\n const m = nameUpper.match(/IN(\\d+)[._-]CU/)\n return Math.min(3 + parseInt(m[1], 10) * 0.1, 5.9)\n }\n\n // ZH-TOP -> GTS (Order 2) (保留兼容性)\n if (nameUpper.startsWith('ZH-TOP') || nameUpper.includes('ZH-TOP')) return 2\n // ZH-BOT -> GBS (Order 7) (保留兼容性)\n if (nameUpper.startsWith('ZH-BOT') || nameUpper.includes('ZH-BOT')) return 7\n // XL-TOP -> GTL (Order 3) (保留兼容性)\n if (nameUpper.startsWith('XL-TOP') || nameUpper.includes('XL-TOP')) return 3\n // XL-BOT -> GBL (Order 6) (保留兼容性)\n if (nameUpper.startsWith('XL-BOT') || nameUpper.includes('XL-BOT')) return 6\n \n // 其他未识别的 .gbr 文件(如 _Drawing, _Drillmap, _NPTH, _Pads, _PTH)排在 GKO 后面(order=10)\n console.log(`文件 ${fileName} 识别为 GBR (未识别), order=10`)\n return 10\n }\n \n // 检查文件名后缀\n if (fileNameUpper.endsWith('_SST') || fileNameUpper.includes('_SST.')) return 0\n if (fileNameUpper.endsWith('_PMT') || fileNameUpper.includes('_PMT.')) return 1\n if (fileNameUpper.endsWith('_SMT') || fileNameUpper.includes('_SMT.')) return 2\n if (fileNameUpper.endsWith('_TOP') || fileNameUpper.includes('_TOP.')) return 3\n if (fileNameUpper.endsWith('_SMB') || fileNameUpper.includes('_SMB.')) return 7\n if (fileNameUpper.endsWith('_PMB') || fileNameUpper.includes('_PMB.')) return 7.5\n if (fileNameUpper.endsWith('_SSB') || fileNameUpper.includes('_SSB.')) return 8\n if (fileNameUpper.includes('OUTLINE')) return 9\n \n // _INT+数字\n const intMatch = fileNameUpper.match(/_INT(\\d+)$/i) || fileNameUpper.match(/_INT(\\d+)\\./i)\n if (intMatch) {\n const number = parseInt(intMatch[1], 10)\n return 100 + number\n }\n \n // 扩展名检查\n // 特殊 DRL 命名:np.drl(DRL2)排在 p.drl(DRL)之后,且与 TXT 钻孔同一段落\n if ((ext === 'DRL' || ext === 'D') && fileNameOnly.toUpperCase() === 'NP.DRL') {\n return 9.65\n }\n if ((ext === 'DRL' || ext === 'D') && fileNameOnly.toUpperCase() === 'P.DRL') {\n return 9.6\n }\n if (layerTypeOrder[ext] !== undefined) {\n return layerTypeOrder[ext]\n }\n \n // KiCad InN_Cu + 扩展名 .g2/.g3:图层顺序以内层序号为准(与列表显示 SIG(N+1) 一致)\n const inCuForOrder = fileNameUpper.match(/IN(\\d+)[._-]CU/i)\n if (inCuForOrder && /^G\\d+$/i.test(ext)) {\n return Math.min(3 + parseInt(inCuForOrder[1], 10) * 0.1, 5.9)\n }\n\n // G+数字按照升序排在 GTL 和 GBL 之间(3.1-5.9)\n const gNumberMatch = ext.match(/^G(\\d+)$/i)\n if (gNumberMatch) {\n const number = parseInt(gNumberMatch[1], 10)\n return Math.min(3 + number * 0.1, 5.9)\n }\n \n // GP+数字排在 G+数字之后,GBL 之前(5.91-5.99)\n const gpNumberMatch = ext.match(/^GP(\\d+)$/i)\n if (gpNumberMatch) {\n const number = parseInt(gpNumberMatch[1], 10)\n return Math.min(5.9 + number * 0.01, 5.99)\n }\n \n // INT+数字(扩展名格式)\n const intNumberMatch = ext.match(/^INT(\\d+)$/i)\n if (intNumberMatch) {\n const number = parseInt(intNumberMatch[1], 10)\n return 100 + number\n }\n \n // GD+数字 排在 GDD (12) 和 GPB (14.5) 之间(14.1-14.4)\n const gdNumberMatch = ext.match(/^GD(\\d+)$/i)\n if (gdNumberMatch) {\n const number = parseInt(gdNumberMatch[1], 10)\n return Math.min(14 + number * 0.1, 14.4)\n }\n \n // GG+数字 排在 GDD (12) 和 GPB (14.5) 之间(14.1-14.4)\n const ggNumberMatch = ext.match(/^GG(\\d+)$/i)\n if (ggNumberMatch) {\n const number = parseInt(ggNumberMatch[1], 10)\n return Math.min(14 + number * 0.1, 14.4)\n }\n \n // GM+数字 排在 GM (14) 后面,GPB (14.5) 之前\n const gmNumberMatch = ext.match(/^GM(\\d+)$/i)\n if (gmNumberMatch) {\n const number = parseInt(gmNumberMatch[1], 10)\n if (number === 1) {\n // GM1 排在 GG1 之后,使用 14.2\n return 14.2\n } else {\n // GM2, GM3... 使用 14 + number * 0.1,最多到 14.4\n return Math.min(14 + number * 0.1, 14.4)\n }\n }\n \n // 检查是否是 SLOT(TXT 文件且文件名包含 SlotHoles)\n const isTxFile = /^TX\\d*$/i.test(ext) || ext === 'TXT'\n if (isTxFile && /slotholes/i.test(fileName)) {\n console.log(`文件 ${fileName} 识别为 SLOT, order=9.5`)\n return 9.5 // SLOT 在 GKO 之后\n }\n \n // 检查是否是 VIA(文件名包含 Via)\n if (/[_\\s]via/i.test(fileName) && isTxFile) {\n console.log(`文件 ${fileName} 识别为 VIA, order=11`)\n return 11\n }\n \n // TXT 文件(钻孔文件,但不是 SlotHoles 和 Via)\n if (isTxFile) {\n console.log(`文件 ${fileName} 识别为 DRL (TXT), order=9.6`)\n return 9.6 // DRL_TXT\n }\n \n // .art 文件:排在最后(order=100)\n if (ext === 'ART') {\n const dot = fileNameOnly.lastIndexOf('.')\n const stemUpper = (dot > 0 ? fileNameOnly.substring(0, dot) : fileNameOnly).toUpperCase()\n if (stemUpper === 'SST') return 0 // GTO\n if (stemUpper === 'SMT') return 2 // GTS\n if (stemUpper === 'TOP') return 3 // GTL\n if (stemUpper === 'BOT') return 6 // GBL\n if (stemUpper === 'SMB') return 7 // GBS\n if (stemUpper === 'SSB') return 8 // GBO\n if (stemUpper === 'OUT') return 9 // GKO\n console.log(`文件 ${fileName} 识别为 ART(unknown), order=100`)\n return 100\n }\n \n // .pho 文件:临时返回 100,后处理会根据同前缀文件的数字比较来更新 order\n // 后处理逻辑见 updatePhoLayerTypes 函数\n if (ext === 'PHO') {\n console.log(`文件 ${fileName} 识别为 PHO (临时order=100,后处理会更新)`)\n return 100\n }\n \n // .drl, .d, .dri 文件:钻孔文件,排在 200\n if (ext === 'DRL' || ext === 'D' || ext === 'DRI') {\n console.log(`文件 ${fileName} 识别为 DRL, order=200`)\n return 200\n }\n \n // .rou 文件:铣边文件,排在 11.5\n if (ext === 'ROU') {\n console.log(`文件 ${fileName} 识别为 ROU, order=11.5`)\n return 11.5\n }\n \n // CAM350 等软件导出的扩展名\n if (ext === 'BOT') return 6 // GBL\n if (ext === 'TOP') return 3 // GTL\n if (ext === 'SOB') return 7 // GBS (Solder mask Bottom)\n if (ext === 'SOT') return 2 // GTS (Solder mask Top)\n if (ext === 'SST') return 0 // GTO (Silkscreen Top)\n if (ext === 'SSB') return 8 // GBO (Silkscreen Bottom)\n if (ext === 'SMT') return 2 // GTS (Solder Mask Top)\n if (ext === 'SMB') return 7 // GBS (Solder Mask Bottom)\n if (ext === 'PMT') return 1 // GTP (Paste Mask Top)\n if (ext === 'PMB') return 7.5 // GBP (Paste Mask Bottom)\n \n // .ser 等直接显示文件名的图层,排在最后(order 1000)\n if (ext === 'SER') {\n console.log(`文件 ${fileName} 识别为 SER (显示文件名), order=1000`)\n return 1000\n }\n \n // 默认值:未识别的图层排在最后\n return 100\n}\n\nfunction animate() {\n render()\n requestAnimationFrame(animate)\n}\n\nfunction render() {\n // 使用纯黑背景,配合加法混合模式\n gl.clearColor(0.0, 0.0, 0.0, 1)\n // 同时清除颜色缓冲和模版缓冲\n gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT)\n \n if (layers.length === 0) return\n \n gl.useProgram(program)\n gl.enableVertexAttribArray(positionLocation)\n \n // 启用模版测试\n gl.enable(gl.STENCIL_TEST)\n\n const aspect = canvas.width / canvas.height\n const matrix = [\n scale / aspect, 0, 0, 0,\n 0, scale, 0, 0,\n 0, 0, 1, 0,\n transX, transY, 0, 1\n ]\n gl.uniformMatrix4fv(uMatrixLocation, false, matrix)\n\n // --- Polarity (LPD/LPC) 支持 ---\n // Altium 等会在铜层里使用 %LPC*% / %LPD*% 表示“挖空/开窗”。\n // direct parser 会把每段 polarity 对应的 index 区间记录在 layer.polarityRuns 中。\n // 这里用 stencil 把 mask 做出来:dark 写 1,clear 写 0,然后用全屏 quad 按 stencil==1 上色。\n const layerHasClearPolarity = (layer) => {\n return Array.isArray(layer.polarityRuns) &&\n layer.polarityRuns.some(r => r && r.polarity === 'clear' && (r.end - r.start) > 0)\n }\n\n const drawLayer = (layer, blendFunc, alpha) => {\n if (layerHasClearPolarity(layer) && quadVertexBuffer && quadIndexBuffer) {\n // 1) 先构建 stencil mask(不写颜色)\n gl.clear(gl.STENCIL_BUFFER_BIT)\n gl.colorMask(false, false, false, false)\n gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)\n\n // 用当前世界矩阵绘制到 stencil\n gl.uniformMatrix4fv(uMatrixLocation, false, matrix)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, layer.vertexBuffer)\n gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, layer.indexBuffer)\n\n for (const run of layer.polarityRuns) {\n if (!run) continue\n const count = (run.end - run.start)\n if (count <= 0) continue\n const ref = run.polarity === 'clear' ? 0 : 1\n gl.stencilFunc(gl.ALWAYS, ref, 0xFF)\n gl.drawElements(gl.TRIANGLES, count, gl.UNSIGNED_INT, run.start * 4)\n }\n\n // 2) 再根据 stencil==1 上色(用全屏 quad),clear 区域自然变“透明孔洞”\n gl.colorMask(true, true, true, true)\n gl.stencilFunc(gl.EQUAL, 1, 0xFF)\n gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP)\n\n gl.blendEquation(gl.FUNC_ADD)\n gl.blendFunc(...blendFunc)\n\n gl.uniform3fv(uColorLocation, layer.color)\n gl.uniform1f(uAlphaLocation, alpha)\n\n // quad 用 identity matrix(直接覆盖屏幕),由 stencil 负责裁剪\n gl.uniformMatrix4fv(uMatrixLocation, false, IDENTITY_MATRIX)\n gl.bindBuffer(gl.ARRAY_BUFFER, quadVertexBuffer)\n gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quadIndexBuffer)\n gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)\n\n // 恢复世界矩阵,避免影响后续 layer\n gl.uniformMatrix4fv(uMatrixLocation, false, matrix)\n return\n }\n\n // 默认路径:直接画几何\n gl.clear(gl.STENCIL_BUFFER_BIT)\n gl.stencilFunc(gl.NOTEQUAL, 1, 0xFF)\n gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE)\n\n gl.blendEquation(gl.FUNC_ADD)\n gl.blendFunc(...blendFunc)\n\n gl.uniform3fv(uColorLocation, layer.color)\n gl.uniform1f(uAlphaLocation, alpha)\n\n gl.bindBuffer(gl.ARRAY_BUFFER, layer.vertexBuffer)\n gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0)\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, layer.indexBuffer)\n gl.drawElements(gl.TRIANGLES, layer.vertexCount, gl.UNSIGNED_INT, 0)\n }\n \n // 渲染:大多数层按“叠加/正常混合”绘制。\n // 只有“开窗类”层(阻焊/锡膏)才使用差值/异或混合去表达“孔洞/镂空”的视觉效果。\n //\n // 重要:GBL/铜层不应使用差值混合,否则同一层内的重叠(三角化区域 + 线段/焊盘等)\n // 会被“异或”成黑色,导致你看到的“红底+黑色走线”的反相效果。\n // 差值/异或混合层:\n // - 阻焊/锡膏:用于“开窗/镂空”\n // - SIGn:用户指定需使用差值/异或(内层实验/对比用)\n //\n // 说明:GBL 不使用差值(否则会把其它层颜色“反相/抵消”,偏离常规Gerber Viewer 的堆叠观感)\n const diffBlendNames = ['GTS', 'GBS', 'GTP', 'GBP', 'GPT', 'GPB']\n const diffIndices = []\n const drillIndices = []\n const gdgggmIndices = []\n \n // 非差值层、非钻孔层\n for (let i = layers.length - 1; i >= 0; i--) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n \n const displayName = getLayerDisplayName(layer)\n const order = layer.order !== undefined ? layer.order : 100\n // GD/GG/GM+数字:\n // - GD/GG:作为对齐/说明类图层,保持置顶(在钻孔层之上)\n // - GM1:通常被当作轮廓/机械 1 层,保持置顶(历史行为)\n // - 其它 GMn(例如 GM15):用户期望在 GPT 之下,不应置顶覆盖锡膏层\n const nameUpper = layer.name.toUpperCase()\n const isGdNum = /\\.GD\\d+$/.test(nameUpper)\n const isGgNum = /\\.GG\\d+$/.test(nameUpper)\n const gmNumMatch = nameUpper.match(/\\.GM(\\d+)$/)\n const isGm1 = gmNumMatch && gmNumMatch[1] === '1'\n const isGdGgGmNum = isGdNum || isGgNum || isGm1\n if (isGdGgGmNum) {\n gdgggmIndices.push(i)\n continue\n }\n if (layer.isDrillFile || layer.isSlotFile) {\n drillIndices.push(i)\n continue\n }\n // SIGn:用户要求按“差值/异或”叠加(与其它软件保持一致)\n // 注意:这是基于 framebuffer dst 的颜色混合,会导致“勾选/取消底层(如GBL)改变结果”的现象,这是预期行为。\n const isSigInner = /^SIG\\d+$/i.test(displayName)\n const isDiffBlend = diffBlendNames.includes(displayName) || isSigInner\n if (isDiffBlend) {\n diffIndices.push(i)\n continue\n }\n \n const additiveNames = ['GTS', 'GBS', 'GTL', 'GBL', 'GPT', 'GBP', 'GTP', 'GPB']\n const isAdditiveLayer = additiveNames.includes(displayName) || (order >= 2.0 && order <= 15.0)\n const isSpecialLayer = (Math.abs(order - 0) < 0.1) || (Math.abs(order - 1) < 0.1)\n const alpha = 1.0\n \n let blendFunc = null\n if (isSpecialLayer) {\n blendFunc = [gl.ONE_MINUS_DST_COLOR, gl.ONE_MINUS_SRC_COLOR]\n } else if (isAdditiveLayer) {\n blendFunc = [gl.ONE, gl.ONE]\n } else {\n blendFunc = [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA]\n }\n\n drawLayer(layer, blendFunc, alpha)\n }\n \n // 差值层单独绘制(差值/异或)\n for (const i of diffIndices.reverse()) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n const alpha = 1.0\n drawLayer(layer, [gl.ONE_MINUS_DST_COLOR, gl.ONE_MINUS_SRC_COLOR], alpha)\n }\n\n // 钻孔层单独绘制(置顶)\n for (const i of drillIndices.reverse()) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n const alpha = 1.0\n drawLayer(layer, [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA], alpha)\n }\n\n // GD/GG/GM+数字 层再绘制,置于钻孔层之上\n for (const i of gdgggmIndices.reverse()) {\n const layer = layers[i]\n if (!layer.visible || !layer.vertexBuffer || !layer.indexBuffer) continue\n const alpha = 1.0\n drawLayer(layer, [gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA], alpha)\n }\n \n gl.disable(gl.STENCIL_TEST)\n}\n\n/**\n * 统一的图层排序函数\n * 对于 SIG 图层,按照 SIG 后面的数字大小排序(忽略 order)\n * 对于其他图层,按照 order 值排序\n */\nfunction sortLayersByOrder(layers) {\n return layers.map((layer, originalIndex) => ({\n layer,\n originalIndex,\n order: layer.order !== undefined ? layer.order : 100,\n displayName: getLayerDisplayName(layer)\n })).sort((a, b) => {\n // 检查是否是 SIG 图层\n const sigMatchA = a.displayName.match(/^SIG(\\d+)$/i)\n const sigMatchB = b.displayName.match(/^SIG(\\d+)$/i)\n \n // 如果两个都是 SIG 图层,直接按照 SIG 后面的数字大小排序(忽略 order)\n if (sigMatchA && sigMatchB) {\n const numA = parseInt(sigMatchA[1], 10)\n const numB = parseInt(sigMatchB[1], 10)\n return numA - numB\n }\n \n // 如果只有一个是 SIG 图层,按 order 排序(SIG 图层应该在 GTL 和 GBL 之间)\n if (sigMatchA || sigMatchB) {\n // 如果一个是 SIG,另一个不是,按 order 排序\n // SIG 图层的 order 应该在 3.1-5.9 范围内\n return a.order - b.order\n }\n \n // 如果都不是 SIG 图层,按 order 排序\n const orderDiff = a.order - b.order\n if (orderDiff !== 0) return orderDiff\n \n // 如果 order 相同且都不是 SIG,按原始索引排序(保持稳定性)\n return a.originalIndex - b.originalIndex\n })\n}\n\n/**\n * 根据 order 重新分配颜色\n * 确保按 order 排序后,第一个图层是红色(baseColors[0]),第二个是绿色(baseColors[1]),以此类推\n */\nfunction reassignColorsByOrder(layers) {\n // 使用统一的排序函数\n const sortedLayers = sortLayersByOrder(layers)\n \n // 按排序后的顺序重新分配颜色\n sortedLayers.forEach(({ layer, originalIndex }, colorIndex) => {\n const hexColor = pickColor(colorIndex)\n const rgb = hexToRgb(hexColor)\n \n // 更新图层的颜色属性\n layer.hexColor = hexColor\n layer.color = [rgb.r, rgb.g, rgb.b]\n \n // 更新 layerColorMap (使用原始索引)\n layerColorMap.set(originalIndex, hexColor)\n \n console.log(`[颜色重新分配] ${layer.name}: order=${layer.order}, originalIndex=${originalIndex}, colorIndex=${colorIndex}, color=${hexColor}`)\n })\n}\n\n/**\n * 更新 .pho 文件的图层类型\n * 根据同前缀文件的数字比较:数字最小的为\"小\"(上层),其他的为\"大\"(下层)\n */\nfunction updatePhoLayerTypes(layers) {\n // 收集所有 .pho 文件\n const phoFiles = layers.filter(layer => {\n const fileNameOnly = (layer.name || '').split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n return fileExtension === '.PHO'\n })\n \n if (phoFiles.length === 0) return\n \n // 按前缀分组\n const prefixGroups = {}\n for (const layer of phoFiles) {\n const fileNameOnly = (layer.name || '').split(/[/\\\\]/).pop()\n const nameUpper = fileNameOnly.toUpperCase()\n \n // 提取前缀和数字\n let prefix = null\n let number = null\n \n if (nameUpper.startsWith('SST')) {\n prefix = 'sst'\n const match = nameUpper.match(/^SST(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('SMD')) {\n prefix = 'smd'\n const match = nameUpper.match(/^SMD(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('SM')) {\n prefix = 'sm'\n const match = nameUpper.match(/^SM(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('ART')) {\n prefix = 'art'\n const match = nameUpper.match(/^ART(\\d+)/)\n number = match ? parseInt(match[1], 10) : 0\n } else if (nameUpper.startsWith('SSB')) {\n prefix = 'ssb'\n number = 0 // ssb 不需要比较数字\n } else if (nameUpper.startsWith('DRL')) {\n prefix = 'drl'\n number = 0 // drl 不需要比较数字\n } else if (nameUpper.startsWith('DD')) {\n prefix = 'dd'\n number = 0 // dd 不需要比较数字\n }\n \n if (prefix) {\n if (!prefixGroups[prefix]) {\n prefixGroups[prefix] = []\n }\n prefixGroups[prefix].push({ layer, number, fileNameOnly })\n }\n }\n \n // 对每个前缀组进行处理\n for (const [prefix, files] of Object.entries(prefixGroups)) {\n // 按数字排序\n files.sort((a, b) => a.number - b.number)\n \n // 数字最小的为\"小\"(上层),其他的为\"大\"(下层)\n for (let i = 0; i < files.length; i++) {\n const { layer, number } = files[i]\n const isSmallest = i === 0\n \n let layerType = null\n let order = null\n \n switch (prefix) {\n case 'sst':\n // sst 开头 → GTO (order=0)\n layerType = 'GTO'\n order = 0\n break\n case 'smd':\n // smd 开头,数字最小 → GTP (order=1),其他 → GBP (order=7.5)\n if (isSmallest) {\n layerType = 'GTP'\n order = 1\n } else {\n layerType = 'GBP'\n order = 7.5\n }\n break\n case 'sm':\n // sm 开头,数字最小 → GTS (order=2),其他 → GBS (order=7)\n if (isSmallest) {\n layerType = 'GTS'\n order = 2\n } else {\n layerType = 'GBS'\n order = 7\n }\n break\n case 'art':\n // art 开头,数字最小 → GTL (order=3),其他 → GBL (order=6)\n if (isSmallest) {\n layerType = 'GTL'\n order = 3\n } else {\n layerType = 'GBL'\n order = 6\n }\n break\n case 'ssb':\n // ssb 开头 → GBO (order=8)\n layerType = 'GBO'\n order = 8\n break\n case 'drl':\n // drl 开头 → DRL (order=200)\n layerType = 'DRL'\n order = 200\n break\n case 'dd':\n // dd 开头 → 显示完整文件名 (order=201,排在 DRL 后面)\n // 不设置 layerType,让 getLayerDisplayName 返回完整文件名\n layerType = null\n order = 201\n break\n }\n \n if (order !== null) {\n // 更新 order\n layer.order = order\n // 如果有 layerType,存储图层类型\n if (layerType) {\n layer.phoLayerType = layerType\n console.log(`[PHO图层识别] ${layer.name}: 前缀=${prefix}, 数字=${number}, 类型=${layerType}, order=${order}`)\n } else {\n console.log(`[PHO图层识别] ${layer.name}: 前缀=${prefix}, 数字=${number}, 显示完整文件名, order=${order}`)\n }\n }\n }\n }\n}\n\n// --- File Handling ---\n\nfileInput.addEventListener('change', async (e) => {\n const file = e.target.files[0]\n if (!file) return\n \n showStatus(`Processing ${file.name}...`, 'loading')\n showFileInfo(file.name, file.size)\n showLoading()\n \n let filesToProcess = []\n const ext = '.' + file.name.split('.').pop().toLowerCase()\n \n try {\n if (ext === '.zip') {\n filesToProcess = await extractZip(file)\n } else if (ext === '.rar') {\n filesToProcess = await extractRar(file)\n } else {\n filesToProcess = [file]\n }\n \n if (filesToProcess.length === 0) {\n showStatus('No Gerber files found.', 'error')\n hideLoading()\n return\n }\n \n // 按照 order 排序文件\n filesToProcess.sort((a, b) => getLayerOrder(a.name) - getLayerOrder(b.name))\n \n // 清空图层和 solo 模式\n layers = []\n layerList.innerHTML = ''\n layerColorMap.clear()\n isSoloMode = false\n soloLayerOrder = []\n soloTargetLayerIndex = null\n cacheNeedsUpdate = true\n \n // 强制重绘一次以清空画布\n render() \n \n let colorIndex = 0 // 颜色索引,只为成功解析的文件分配\n const pendingLayers = [] // 临时存储解析好的图层,避免解析过程中频繁刷新导致画面跳动\n \n // 处理所有文件\n for (let i = 0; i < filesToProcess.length; i++) {\n const f = filesToProcess[i]\n showStatus(`Parsing ${i+1}/${filesToProcess.length}: ${f.name}...`, 'loading')\n \n // 检查文件类型\n const fileExt = '.' + f.name.split('.').pop().toLowerCase()\n \n // 读取文件内容\n const text = await f.text()\n \n // 从 Gerber 文件提取 %FS/%MO 作为后续 Excellon(TXT/DRL) 的格式兜底\n // 注意:函数内部会自动跳过包含 M48 的钻孔文件\n updateInheritedGerberFormatHintFromText(text, f.name)\n \n // 对于 TXT 和 TX+数字 文件,检查是否是 Excellon 格式(M48 开头)\n const isTxFile = /^\\.tx\\d*$/i.test(fileExt) || fileExt === '.txt'\n const isGbrFile = fileExt === '.gbr'\n const isPhoFile = fileExt === '.pho'\n const isArtFile = fileExt === '.art'\n const isDrlFile = fileExt === '.drl' || fileExt === '.d' || fileExt === '.dri'\n const isRouFile = fileExt === '.rou'\n let isM48DrillFile = false\n let isSlotFile = false\n let useWebGerber = false\n let useDirectParser = false\n \n if (isTxFile) {\n // 检查是否是 SlotHoles 文件\n if (/slotholes/i.test(f.name)) {\n isSlotFile = true\n console.log(`[SLOT] ${f.name} 是 SlotHoles 文件`)\n }\n \n // 检查前10行是否包含 M48(Excellon 钻孔文件头)\n // 有些文件第一行是 M72(英制单位)或其他指令,M48 在第二行\n const lines = text.trim().split('\\n').slice(0, 10)\n const hasM48 = lines.some(line => /^M48$/i.test(line.trim()))\n \n if (hasM48) {\n isM48DrillFile = true\n useWebGerber = true\n if (isSlotFile) {\n console.log(`[SLOT] ${f.name} 是 Excellon SlotHoles 文件(含M48),使用 web-gerber 解析`)\n } else {\n console.log(`[钻孔] ${f.name} 是 Excellon 钻孔文件(含M48),使用 web-gerber 解析`)\n }\n } else {\n console.log(`[Gerber] ${f.name} 是 TXT 文件但不含 M48,使用 web-gerber 解析`)\n useWebGerber = true\n }\n } else if (isGbrFile) {\n // .gbr 文件使用 web-gerber 解析\n console.log(`[Gerber] ${f.name} 是 GBR 文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (isPhoFile) {\n // .pho 文件使用 web-gerber 解析\n console.log(`[Gerber] ${f.name} 是 PHO 文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (isArtFile) {\n // .art 文件使用 web-gerber 解析\n console.log(`[Gerber] ${f.name} 是 ART 文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (isDrlFile) {\n // .drl 和 .d 文件使用 web-gerber 解析(Excellon 钻孔文件)\n console.log(`[钻孔] ${f.name} 是 DRL 钻孔文件,使用 web-gerber 解析`)\n isM48DrillFile = true\n useWebGerber = true\n } else if (isRouFile) {\n // .rou 文件使用 web-gerber 解析(铣边文件)\n console.log(`[铣边] ${f.name} 是 ROU 铣边文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else {\n // CAM350 等软件导出的扩展名,使用 web-gerber 解析\n const isCam350File = ['.bot', '.top', '.sob', '.sot', '.ser', '.sst', '.ssb', '.smt', '.smb', '.pmt', '.pmb'].includes(fileExt)\n if (isCam350File) {\n console.log(`[Gerber] ${f.name} 是 CAM350 格式文件,使用 web-gerber 解析`)\n useWebGerber = true\n } else {\n // 对于标准 Gerber 文件,检测是否包含宏定义或区域填充\n // 如果包含宏定义(%AM...%)或区域填充(G36),强制使用 web-gerber\n const hasMacroDefinition = /%AM[A-Z0-9_]+\\*/i.test(text)\n const hasRegionFill = /G36\\*/i.test(text) // G36 开始区域填充\n if (hasMacroDefinition) {\n console.log(`[宏定义] ${f.name} 包含 Aperture Macro 定义,使用 web-gerber 解析`)\n useWebGerber = true\n } else if (hasRegionFill) {\n console.log(`[区域填充] ${f.name} 包含 G36 区域填充命令,使用 web-gerber 解析`)\n useWebGerber = true\n }\n }\n }\n \n try {\n let data\n \n // 准备解析选项 - 检查文件扩展名和文件名判断轮廓层\n const fileNameOnly = f.name.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // 检查扩展名和文件名关键词\n const isOutlineLayer = \n getLayerDisplayName(f.name) === 'GKO' ||\n fileExtension === '.GKO' || \n fileExtension === '.GM' || \n /^\\.GM\\d+$/i.test(fileExtension) ||\n fileNameUpper.includes('OUTLINE') ||\n /EDGE[._-]CUTS/i.test(fileNameUpper) ||\n fileNameUpper.includes('WX') ||\n fileNameUpper.includes('BOARD') ||\n fileNameUpper.startsWith('GKO') ||\n fileNameUpper.startsWith('OUT')\n \n const parserOpts = {\n minLineWidth: isOutlineLayer ? 0.3 : 0 // 轮廓层(GKO、GM1等)最小线宽 0.3mm\n }\n \n // 只在识别为轮廓层时打印日志\n if (isOutlineLayer) {\n console.log(`[轮廓层] ${fileNameOnly}, minLineWidth=${parserOpts.minLineWidth}mm`)\n }\n \n if (useWebGerber) {\n data = await parseWithWebGerber(text, f.name)\n // web-gerber 返回空几何时,对非钻孔文件回退到 GerberDirectParser\n if ((!data.vertices || data.vertices.length === 0) && !isM48DrillFile) {\n console.log(`[回退] ${f.name} web-gerber 无几何数据,尝试 GerberDirectParser`)\n const parser = new GerberDirectParser(parserOpts)\n data = parser.parse(text)\n console.log(`[回退结果] ${f.name}: ${data.vertices.length / 3} 顶点, ${data.triangleIndices.length / 3} 三角形`)\n }\n } else if (useDirectParser) {\n console.log(`[直接解析] 开始解析 ${f.name}`)\n const parser = new GerberDirectParser(parserOpts)\n data = parser.parse(text)\n console.log(`[直接解析] ${f.name}: ${data.vertices.length / 3} 顶点, ${data.triangleIndices.length / 3} 三角形`)\n } else {\n const parser = new GerberDirectParser(parserOpts)\n data = parser.parse(text)\n }\n \n // 检查是否有几何数据\n if (!data.vertices || data.vertices.length === 0) {\n console.log(`[跳过] ${f.name} 没有几何数据`)\n continue\n }\n \n // 对于参考层(GTS/GBS/GTL/GBL),额外用web-gerber解析一次以提取特征点\n let featureExtractionVertices = null\n const refLayerFileName = f.name.split(/[/\\\\]/).pop()\n const refLayerExt = '.' + refLayerFileName.split('.').pop().toUpperCase()\n const refLayerExtName = refLayerExt.substring(1)\n // 使用 getLayerDisplayName 判断逻辑层类型(支持 .pho/.art 等文件名映射)\n const logicalLayerType = getLayerDisplayName(f.name)\n const isRefLayer = ['GTS', 'GBS', 'GTL', 'GBL'].includes(refLayerExtName) || ['GTS', 'GBS', 'GTL', 'GBL'].includes(logicalLayerType)\n \n if (isRefLayer && !useWebGerber) {\n console.log(`[特征提取] ${f.name} (${refLayerExtName}) 额外用web-gerber解析以提取圆心`)\n try {\n // 临时抑制web-gerber的详细日志\n const originalLog = console.log\n const silencedPatterns = ['[web-gerber]', 'plotResult', 'SVG', 'Child']\n console.log = (...args) => {\n const message = args.join(' ')\n if (!silencedPatterns.some(pattern => message.includes(pattern))) {\n originalLog.apply(console, args)\n }\n }\n \n const featureData = await parseWithWebGerber(text, f.name)\n \n // 恢复console.log\n console.log = originalLog\n \n if (featureData && featureData.vertices && featureData.vertices.length > 0) {\n featureExtractionVertices = featureData.vertices\n console.log(`[特征提取] ${f.name} ✓ 提取到 ${featureExtractionVertices.length / 3} 个顶点用于圆心识别`)\n }\n } catch (err) {\n console.log = console.log.bind(console) // 确保恢复\n console.warn(`[特征提取] ${f.name} ✗ web-gerber解析失败:`, err.message)\n }\n }\n \n // 计算当前数据的边界\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n for (let k = 0; k < data.vertices.length; k += 3) {\n const x = data.vertices[k]\n const y = data.vertices[k + 1]\n minX = Math.min(minX, x)\n maxX = Math.max(maxX, x)\n minY = Math.min(minY, y)\n maxY = Math.max(maxY, y)\n }\n const bounds = { minX, minY, maxX, maxY }\n\n // 根据成功解析的文件顺序分配颜色\n // 当颜色用完后,后续图层都使用白色\n const hexColor = pickColor(colorIndex)\n const rgb = hexToRgb(hexColor)\n colorIndex++\n \n // 创建独立的 WebGL 缓冲\n const vertexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, data.vertices, gl.STATIC_DRAW)\n \n const indexBuffer = gl.createBuffer()\n gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)\n gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.triangleIndices, gl.STATIC_DRAW)\n \n // 决定保存哪些顶点数据用于特征提取\n // 优先级:特征提取专用顶点 > 参考层(直接用web-gerber结果) > 渲染顶点(钻孔层)\n let rawVerticesForFeatureExtraction = null\n if (featureExtractionVertices) {\n // 参考层:使用web-gerber生成的特征提取专用顶点\n rawVerticesForFeatureExtraction = featureExtractionVertices\n } else if (isRefLayer && useWebGerber) {\n // 参考层:如果本身就是用 web-gerber 解析的,也需要保留一份顶点用于圆心特征提取/钻孔对齐\n // parseWithWebGerber 已做了 Y 翻转到“世界坐标系(Y向上)”并对 circle 额外注入中心点,适合作为特征输入\n rawVerticesForFeatureExtraction = data.vertices\n } else if (isM48DrillFile || isDrlFile) {\n // 钻孔层:使用渲染顶点\n rawVerticesForFeatureExtraction = data.vertices\n }\n \n pendingLayers.push({\n name: f.name,\n visible: true,\n color: [rgb.r, rgb.g, rgb.b],\n hexColor: hexColor,\n // vertices: Array.from(data.vertices), // 不再保留 CPU 端数据以节省内存\n // indices: Array.from(data.triangleIndices),\n vertexBuffer,\n indexBuffer,\n vertexCount: data.triangleIndices.length,\n // Gerber LP(LPC/LPD)极性段落:用于 stencil 挖空(主要影响铜层大面积铺铜的“避空/开窗”)\n polarityRuns: data.polarityRuns || null,\n // Drill:记录每个孔(或中心+尺寸形状)的顶点区间,用于后续“只缩放孔位、不缩放孔径”的修正\n drillPosScaleRuns: data.drillPosScaleRuns || null,\n order: getLayerOrder(f.name),\n isDrillFile: isM48DrillFile,\n isSlotFile: isSlotFile,\n bounds: bounds,\n // rawVertices用于特征提取(钻孔层用渲染顶点,参考层用web-gerber生成的专用顶点)\n rawVertices: rawVerticesForFeatureExtraction\n })\n \n console.log(`[成功] ${f.name}: ${data.vertices.length / 3} 顶点, ${data.triangleIndices.length / 3} 三角形`)\n \n // 注意:不再立即更新 layers 和 UI,等所有文件解析完再一次性更新\n // 这样可以避免画面跳动和频繁重绘\n \n } catch (error) {\n console.error(`[错误] 解析文件 ${f.name} 失败:`, error.message)\n // 继续处理下一个文件\n }\n \n // 让浏览器有机会更新 UI(显示解析进度)\n if (i < filesToProcess.length - 1) {\n await new Promise(resolve => setTimeout(resolve, 0))\n }\n }\n \n if (pendingLayers.length === 0) {\n showStatus('No geometry generated.', 'error')\n hideLoading()\n return\n }\n \n // 所有文件解析完成,赋值给 layers\n layers = pendingLayers\n \n // 后处理:根据同前缀文件的数字比较来更新 .pho 文件的图层类型\n updatePhoLayerTypes(layers)\n \n // 根据更新后的 order 重新分配颜色,确保第一个图层(order最小)是红色\n reassignColorsByOrder(layers)\n \n // 在显示之前,统一进行钻孔层对齐修正\n // 此时所有图层的 bounds 都已知,可以准确找到参考层\n checkDrillLayerAlignment()\n \n // 计算总三角形数\n const totalTris = layers.reduce((sum, l) => sum + l.vertexCount / 3, 0)\n showStatus(`✓ Rendered ${layers.length} layers (${Math.floor(totalTris)} tris)`, 'success')\n hideLoading()\n \n // 更新 UI 列表\n updateLayerList()\n \n // 标记缓存需要更新并自动居中\n cacheNeedsUpdate = true\n centerView()\n \n } catch (err) {\n console.error(err)\n showStatus('✗ Error: ' + err.message, 'error')\n hideLoading()\n }\n})\n\nfunction showStatus(msg, type = '') {\n status.textContent = msg\n status.className = `status ${type}`\n}\n\nfunction showFileInfo(name, size) {\n fileName.textContent = name\n fileSize.textContent = (size / 1024).toFixed(2) + ' KB'\n fileInfo.classList.add('active')\n}\n\nfunction updateLayerList() {\n layerList.innerHTML = ''\n \n // 使用统一的排序函数\n const sortedLayers = sortLayersByOrder(layers)\n \n // 调试:打印排序后的图层顺序\n console.log('[图层列表排序] 排序后的图层顺序:')\n sortedLayers.forEach(({ layer, originalIndex }, index) => {\n const displayName = getLayerDisplayName(layer)\n console.log(` ${index + 1}. ${displayName} (order=${layer.order || 100}, originalIndex=${originalIndex})`)\n })\n \n sortedLayers.forEach(({ layer, originalIndex }, displayIndex) => {\n const item = document.createElement('div')\n item.className = layer.visible ? 'layer-item' : 'layer-item disabled'\n \n const indexSpan = document.createElement('span')\n indexSpan.className = 'layer-index'\n indexSpan.textContent = displayIndex + 1\n \n const toggle = document.createElement('div')\n toggle.className = `layer-toggle ${layer.visible ? 'checked' : ''}`\n toggle.onclick = (e) => {\n e.stopPropagation()\n toggleLayer(originalIndex) // 使用原始索引\n }\n \n const colorBox = document.createElement('div')\n colorBox.className = 'layer-color'\n if (!layer.visible) {\n colorBox.style.backgroundColor = '#cccccc' // 浅灰色\n } else {\n let displayColor = layer.hexColor\n if (layerColorMap.has(originalIndex)) { // 使用原始索引\n displayColor = layerColorMap.get(originalIndex)\n }\n colorBox.style.backgroundColor = displayColor\n }\n \n const nameSpan = document.createElement('div')\n nameSpan.className = 'layer-name'\n let displayName = getLayerDisplayName(layer)\n // 如果是 SLOT 文件,显示 SLOT\n if (layer.isSlotFile) {\n displayName = 'SLOT'\n }\n // 如果是钻孔文件,根据文件名显示特定名称\n else if (layer.isDrillFile) {\n const fileName = layer.name\n if (/[_\\s]via/i.test(fileName)) {\n displayName = 'VIA'\n } else {\n displayName = 'DRL'\n }\n }\n nameSpan.textContent = displayName\n nameSpan.setAttribute('title', layer.name)\n \n // 双击图层名称:单独显示该图层\n nameSpan.addEventListener('dblclick', (e) => {\n e.stopPropagation()\n soloLayer(originalIndex) // 使用原始索引\n })\n \n item.appendChild(indexSpan)\n item.appendChild(toggle)\n item.appendChild(colorBox)\n item.appendChild(nameSpan)\n layerList.appendChild(item)\n })\n \n updateToggleAllState()\n}\n\n/**\n * 切换图层可见性\n */\nfunction toggleLayer(index) {\n if (index < 0 || index >= layers.length) return\n \n layers[index].visible = !layers[index].visible\n \n // 如果处于 solo 模式\n if (isSoloMode) {\n if (layers[index].visible) {\n // 显示图层:按勾选顺序添加\n if (soloLayerOrder.indexOf(index) === -1) {\n soloLayerOrder.push(index)\n \n // 分配颜色\n if (layerColorMap.has(index)) {\n const existingColor = layerColorMap.get(index)\n updateLayerColor(index, existingColor)\n } else {\n const colorIndex = layerColorMap.size\n const newColor = pickColor(colorIndex)\n layerColorMap.set(index, newColor)\n updateLayerColor(index, newColor)\n }\n } else {\n if (layerColorMap.has(index)) {\n const existingColor = layerColorMap.get(index)\n updateLayerColor(index, existingColor)\n }\n }\n }\n }\n \n updateLayerList()\n cacheNeedsUpdate = true\n}\n\n/**\n * 单独显示图层\n */\nfunction soloLayer(targetLayerIndex) {\n console.log(`单独显示图层 ${targetLayerIndex}`)\n \n // 进入单独显示模式\n isSoloMode = true\n \n // 清空颜色映射和勾选顺序数组\n layerColorMap.clear()\n soloLayerOrder = []\n \n // 隐藏所有图层\n layers.forEach((layer, index) => {\n layer.visible = false\n })\n \n // 只显示目标图层\n layers[targetLayerIndex].visible = true\n \n // 将目标图层添加到勾选顺序数组\n soloLayerOrder.push(targetLayerIndex)\n \n // 保存双击的目标图层索引\n soloTargetLayerIndex = targetLayerIndex\n \n // 目标图层使用 baseColors[0](红色)\n const newColor = baseColors[0]\n layerColorMap.set(targetLayerIndex, newColor)\n updateLayerColor(targetLayerIndex, newColor)\n \n // 更新图层列表\n updateLayerList()\n cacheNeedsUpdate = true\n \n console.log(`图层 ${targetLayerIndex} 颜色重置为: ${newColor}`)\n}\n\n/**\n * 更新图层颜色\n */\nfunction updateLayerColor(layerIndex, hexColor) {\n if (layerIndex < 0 || layerIndex >= layers.length) return\n \n const layer = layers[layerIndex]\n const rgb = hexToRgb(hexColor)\n \n // 更新图层的颜色数组(注意这会直接修改原始顶点颜色)\n layer.color = [rgb.r, rgb.g, rgb.b]\n layer.hexColor = hexColor\n}\n\nlayerToggleAll.onclick = () => {\n if (layers.length === 0) return\n const allVisible = layers.every(l => l.visible)\n const newVisible = !allVisible\n \n // 如果处于 solo 模式且是全部显示操作\n if (isSoloMode && !allVisible) {\n // 按照排序顺序(order 值从小到大)重新分配颜色\n const sortedLayers = layers.map((layer, idx) => ({ layer, idx }))\n .sort((a, b) => (a.layer.order || 100) - (b.layer.order || 100))\n \n // 清空并重新填充 soloLayerOrder\n soloLayerOrder = []\n \n // 颜色分配索引(从1开始,因为双击的图层使用baseColors[0])\n let colorIndex = 1\n \n // 按照排序顺序依次分配颜色\n sortedLayers.forEach(({ layer, idx }) => {\n layer.visible = true\n \n // 如果是双击的目标图层,使用 baseColors[0](红色)\n if (idx === soloTargetLayerIndex) {\n const newColor = baseColors[0]\n layerColorMap.set(idx, newColor)\n updateLayerColor(idx, newColor)\n soloLayerOrder.push(idx)\n } else {\n // 其他图层按照排序顺序从 baseColors[1] 开始分配颜色\n const newColor = pickColor(colorIndex)\n layerColorMap.set(idx, newColor)\n updateLayerColor(idx, newColor)\n soloLayerOrder.push(idx)\n colorIndex++\n }\n })\n \n console.log('solo模式下全部显示,双击的图层保持红色,其余图层按排序顺序分配颜色')\n } else {\n // 非 solo 模式或全部隐藏操作\n layers.forEach(layer => {\n layer.visible = newVisible\n })\n }\n \n updateLayerList()\n cacheNeedsUpdate = true\n}\n\nfunction updateToggleAllState() {\n const allVisible = layers.every(l => l.visible)\n if (allVisible) {\n layerToggleAllCheckbox.classList.add('checked')\n } else {\n layerToggleAllCheckbox.classList.remove('checked')\n }\n}\n\nasync function extractZip(file) {\n const files = []\n try {\n const zip = await JSZip.loadAsync(file)\n for (const [filename, zipEntry] of Object.entries(zip.files)) {\n if (!zipEntry.dir && isGerberFile(filename)) {\n const blob = await zipEntry.async('blob')\n files.push(new File([blob], filename))\n }\n }\n } catch (e) {\n console.error(e)\n throw new Error('Failed to unzip')\n }\n return files\n}\n\nfunction isGerberFile(fileName) {\n // 提取文件名(去掉路径,只保留文件名部分)\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileNameUpper = fileNameOnly.toUpperCase()\n \n // 先检查文件名后缀模式(优先级最高)\n const specialSuffixes = ['_SST', '_PMT', '_SMT', '_TOP', '_SMB', '_PMB', '_SSB', 'OUTLINE']\n for (const suffix of specialSuffixes) {\n if (fileNameUpper.endsWith(suffix) || fileNameUpper.includes(suffix + '.')) {\n return true\n }\n }\n \n // 检查 _INT+数字 模式\n if (/.*_INT\\d+$/i.test(fileNameUpper) || /.*_INT\\d+\\./i.test(fileNameUpper)) {\n return true\n }\n \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 // 新增:CAM350 等软件导出的扩展名\n '.bot', '.top', '.sob', '.sot', '.dri', '.ser',\n '.sst', '.ssb', '.smt', '.smb', '.pmt', '.pmb'\n ]\n const fileExtension = '.' + fileNameOnly.split('.').pop().toLowerCase()\n return validExtensions.includes(fileExtension) || \n /^\\.g\\d+$/i.test(fileExtension) || \n /^\\.gm\\d+$/i.test(fileExtension) ||\n /^\\.gp\\d+$/i.test(fileExtension) ||\n /^\\.tx\\d+$/i.test(fileExtension) ||\n /^\\.int\\d+$/i.test(fileExtension)\n}\n\n/**\n * 检测和修正钻孔层错位\n * 在所有图层解析完成后调用,确保参考层可用\n */\nfunction checkDrillLayerAlignment() {\n // 查找钻孔层\n const drillLayers = layers.filter(layer => layer.isDrillFile || layer.isSlotFile)\n \n if (drillLayers.length === 0) {\n console.log('[钻孔对齐] 未找到钻孔层,跳过')\n return\n }\n \n // 组合钻孔层边界(用于 sanity check / GKO fallback)\n const combineBounds = (layersToCombine) => {\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const l of layersToCombine) {\n if (!l || !l.bounds) continue\n minX = Math.min(minX, l.bounds.minX)\n minY = Math.min(minY, l.bounds.minY)\n maxX = Math.max(maxX, l.bounds.maxX)\n maxY = Math.max(maxY, l.bounds.maxY)\n }\n if (!Number.isFinite(minX) || !Number.isFinite(minY) || !Number.isFinite(maxX) || !Number.isFinite(maxY)) {\n return null\n }\n return { minX, minY, maxX, maxY }\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 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 // 查找GKO(或 outline)层,用于校验/兜底对齐\n const outlineLayer = layers.find(layer => {\n const fileNameOnly = (layer.name || '').split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const upper = fileNameOnly.toUpperCase()\n // 兼容:有些工程没有 GKO,轮廓在 GM1/GMxx(Mechanical)里\n return ext === 'GKO' ||\n ext === 'GM' ||\n /^GM\\d+$/i.test(ext) ||\n upper.includes('_PROFILE') ||\n upper.includes('PROFILE') ||\n upper.includes('OUTLINE') ||\n upper.includes('BOARD') ||\n upper.startsWith('GKO')\n })\n\n let outlineBounds = outlineLayer && outlineLayer.bounds ? outlineLayer.bounds : null\n let combinedDrillBounds = combineBounds(drillLayers)\n\n if (!combinedDrillBounds) {\n console.log('[钻孔对齐] 无法计算钻孔层 bounds,跳过')\n return\n }\n\n // ==================== 2d 对齐逻辑的关键差异:钻孔坐标“倍数错误”兜底(只缩放孔位,不缩放孔径)====================\n // 某些 Excellon(.TXT/.DRL) 会在头部写 INCH,TZ,但实际坐标更像 LZ(或整体小数点错位),导致 X/Y 解析出来扩大 10/100 倍。\n // 2d 版本里有 checkAndScaleDrillLayers:如果钻孔 bounds 比参考层大很多就除以 10(孔径不变)。\n // 2d-direct 这里做等价修正,但更安全:\n // - 用参考层 bounds 计算倍率\n // - 仅当倍率接近 10^n 且修正后接近 1x 时才应用\n // - 仅移动每个孔的“中心”,不缩放孔径/半径(通过 drillPosScaleRuns 记录的顶点区间实现)\n // 用 displayName 作为“逻辑层类型”(支持 .art 的 Top/Bot/SST/SMT/... 映射)\n const getLayerType = (name) => getLayerDisplayName(name)\n const sizeOfBounds = (b) => Math.max(b.maxX - b.minX, b.maxY - b.minY)\n\n // ⚠️ 重要兜底:若 GKO/Outline 尺寸远大于铜层(例如 >5x),通常意味着轮廓文件坐标/单位异常\n // 或者是面板边框/工装边,直接拿它做对齐会把钻孔“拉偏”且让视图被撑大。\n // 这种情况下:对齐参考优先信任 GTL/GBL/GTS/GBS,而不是 Outline。\n if (outlineBounds) {\n const priority = ['GTL', 'GBL', 'GTS', 'GBS']\n let ref = null\n for (const t of priority) {\n const l = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile && getLayerType(x.name) === t)\n if (l) { ref = { name: l.name, bounds: l.bounds, reason: t }; break }\n }\n if (!ref) {\n const any = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile)\n if (any) ref = { name: any.name, bounds: any.bounds, reason: 'any' }\n }\n if (ref && ref.bounds) {\n const outlineSize = sizeOfBounds(outlineBounds)\n const refSize = sizeOfBounds(ref.bounds)\n const ratio = refSize > 0 ? (outlineSize / refSize) : Infinity\n if (Number.isFinite(ratio) && ratio > 5) {\n console.warn(`[钻孔对齐] ⚠️ Outline(${outlineLayer?.name || 'unknown'}) 尺寸=${outlineSize.toFixed(3)}mm 远大于参考层(${ref.reason}): ${ref.name} 尺寸=${refSize.toFixed(3)}mm (ratio=${ratio.toFixed(2)}),判定为“不可信Outline”,将跳过GKO兜底并用铜层做对齐参考。`)\n outlineBounds = null\n }\n }\n }\n const pickReferenceForScale = () => {\n if (outlineBounds) return { name: outlineLayer.name, bounds: outlineBounds, reason: 'outline' }\n const priority = ['GTL', 'GBL', 'GTS', 'GBS']\n for (const t of priority) {\n const l = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile && getLayerType(x.name) === t)\n if (l) return { name: l.name, bounds: l.bounds, reason: t }\n }\n const any = layers.find(x => x && x.bounds && !x.isDrillFile && !x.isSlotFile)\n if (any) return { name: any.name, bounds: any.bounds, reason: 'any' }\n return null\n }\n const decidePow10ScaleFix = (ratio) => {\n if (!Number.isFinite(ratio) || ratio <= 0) return 1\n const acceptLow = 0.7\n const acceptHigh = 1.3\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 >= acceptLow && newRatio <= acceptHigh) 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 >= acceptLow && newRatio <= acceptHigh) return cand\n }\n return 1\n }\n // 支持 X/Y 分别缩放(解决 web-gerber 对 DRL 的 X/Y 使用不同格式解析的问题)\n const applyPosScaleFixToDrills = (scaleX, scaleY, tag) => {\n const isUniform = (scaleX === scaleY)\n if (isUniform) {\n console.warn(`[钻孔对齐] ⚠️ 应用钻孔坐标缩放(${tag}): scale=${scaleX}(仅缩放孔位,不缩放孔径)`)\n } else {\n console.warn(`[钻孔对齐] ⚠️ 应用钻孔坐标缩放(${tag}): scaleX=${scaleX}, scaleY=${scaleY}(仅缩放孔位,不缩放孔径)`)\n }\n for (const dl of drillLayers) {\n if (!dl.rawVertices || !dl.drillPosScaleRuns || dl.drillPosScaleRuns.length === 0) continue\n const vertices = dl.rawVertices\n const runs = dl.drillPosScaleRuns\n\n for (const run of runs) {\n if (!run || !(run.count > 0) || run.start == null) continue\n const dx = (run.cx || 0) * (scaleX - 1)\n const dy = (run.cy || 0) * (scaleY - 1)\n const start = run.start * 3\n const end = (run.start + run.count) * 3\n for (let i = start; i < end; i += 3) {\n vertices[i] += dx\n vertices[i + 1] += dy\n }\n // 更新 run 的中心,避免后续误用\n run.cx = (run.cx || 0) * scaleX\n run.cy = (run.cy || 0) * scaleY\n }\n\n // 重新上传顶点\n gl.bindBuffer(gl.ARRAY_BUFFER, dl.vertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)\n\n // 重算 bounds\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n for (let k = 0; k < vertices.length; k += 3) {\n const x = vertices[k]\n const y = vertices[k + 1]\n minX = Math.min(minX, x)\n maxX = Math.max(maxX, x)\n minY = Math.min(minY, y)\n maxY = Math.max(maxY, y)\n }\n dl.bounds = { minX, minY, maxX, maxY }\n\n if (isUniform) {\n console.log(`[钻孔对齐] ✓ ${dl.name} 已应用坐标缩放 scale=${scaleX}`)\n } else {\n console.log(`[钻孔对齐] ✓ ${dl.name} 已应用坐标缩放 scaleX=${scaleX}, scaleY=${scaleY}`)\n }\n }\n }\n\n const refForScale = pickReferenceForScale()\n if (refForScale && refForScale.bounds) {\n const refBounds = refForScale.bounds\n const refWidth = refBounds.maxX - refBounds.minX\n const refHeight = refBounds.maxY - refBounds.minY\n const drillWidth = combinedDrillBounds.maxX - combinedDrillBounds.minX\n const drillHeight = combinedDrillBounds.maxY - combinedDrillBounds.minY\n\n // ===== 防误判:孔簇很小但本来就在板内时,不要触发“倍数缩放” =====\n // 典型案例:只有少量定位孔/边缘孔,drillW/drillH 远小于板子尺寸,但并不代表坐标缩放错误。\n const holeCount = drillLayers.reduce((n, dl) => n + ((dl && dl.drillPosScaleRuns) ? dl.drillPosScaleRuns.length : 0), 0)\n const overflowToRef = computeOverflow(combinedDrillBounds, refBounds)\n const refSize = Math.max(refWidth, refHeight)\n const inRef = overflowToRef.total <= 1e-3\n if (inRef) {\n console.log(`[钻孔对齐] ℹ️ Drill bounds 已在参考层(${refForScale.reason})范围内(holes=${holeCount}, drillW=${drillWidth.toFixed(3)}, drillH=${drillHeight.toFixed(3)}), 跳过 bounds-fit 坐标缩放兜底`)\n } else if (holeCount > 0 && holeCount < 50 && overflowToRef.total < refSize * 0.2) {\n // 小孔数且越界不明显:更倾向于后续“平移/特征匹配”,避免误缩放\n console.log(`[钻孔对齐] ℹ️ Drill 孔数较少(${holeCount})且越界不明显(total=${overflowToRef.total.toFixed(3)}mm),跳过 bounds-fit 坐标缩放兜底`)\n } else {\n \n // 分别计算 X 和 Y 的缩放因子\n const ratioX = drillWidth > 0 && refWidth > 0 ? drillWidth / refWidth : 1\n const ratioY = drillHeight > 0 && refHeight > 0 ? drillHeight / refHeight : 1\n const scaleX = decidePow10ScaleFix(ratioX)\n const scaleY = decidePow10ScaleFix(ratioY)\n \n // 防误判:只允许等比缩放(绝大多数单位/小数点错误都是统一倍率)\n if ((scaleX !== 1 || scaleY !== 1) && scaleX !== scaleY) {\n console.warn(`[钻孔对齐] ⚠️ bounds-fit 推断为非等比(scaleX=${scaleX}, scaleY=${scaleY}),为避免误缩放已忽略;将转而尝试平移/特征点对齐`)\n } else if (scaleX !== 1 || scaleY !== 1) {\n console.warn(`[钻孔对齐] ⚠️ Drill bounds 倍数异常:drillW=${drillWidth.toFixed(3)} drillH=${drillHeight.toFixed(3)} refW=${refWidth.toFixed(3)} refH=${refHeight.toFixed(3)} ratioX=${ratioX.toFixed(2)} ratioY=${ratioY.toFixed(2)} ref=${refForScale.name}(${refForScale.reason}) → 尝试 scaleX=${scaleX} scaleY=${scaleY}`)\n applyPosScaleFixToDrills(scaleX, scaleY, `bounds-fit (ref=${refForScale.reason})`)\n // 更新组合 bounds(用于后续 overflow / 对齐)\n combinedDrillBounds = combineBounds(drillLayers) || combinedDrillBounds\n }\n }\n } else {\n console.log('[钻孔对齐] 未找到参考层 bounds,跳过钻孔坐标缩放兜底')\n }\n \n let overflowBefore = null\n if (outlineBounds) {\n overflowBefore = computeOverflow(combinedDrillBounds, outlineBounds)\n console.log(`[钻孔对齐] GKO/Outline层: ${outlineLayer.name}`)\n console.log(`[钻孔对齐] Drill bounds(before): minX=${combinedDrillBounds.minX.toFixed(3)}, minY=${combinedDrillBounds.minY.toFixed(3)}, maxX=${combinedDrillBounds.maxX.toFixed(3)}, maxY=${combinedDrillBounds.maxY.toFixed(3)}`)\n console.log(`[钻孔对齐] Outline bounds: minX=${outlineBounds.minX.toFixed(3)}, minY=${outlineBounds.minY.toFixed(3)}, maxX=${outlineBounds.maxX.toFixed(3)}, maxY=${outlineBounds.maxY.toFixed(3)}`)\n console.log(`[钻孔对齐] 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)} (mm)`)\n } else {\n console.log('[钻孔对齐] 未找到GKO/Outline层,将仅使用特征点匹配对齐(若可用)')\n }\n\n // 统一应用偏移到所有钻孔层\n const applyOffsetToAllDrills = (dx, dy, tag) => {\n console.log(`[钻孔对齐] ⚠️ 应用全局偏移(${tag}): X=${dx.toFixed(3)}mm, Y=${dy.toFixed(3)}mm`)\n for (const dl of drillLayers) {\n if (!dl.rawVertices) continue\n\n const vertices = dl.rawVertices\n for (let k = 0; k < vertices.length; k += 3) {\n vertices[k] += dx\n vertices[k + 1] += dy\n }\n\n gl.bindBuffer(gl.ARRAY_BUFFER, dl.vertexBuffer)\n gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)\n\n if (dl.bounds) {\n dl.bounds.minX += dx\n dl.bounds.maxX += dx\n dl.bounds.minY += dy\n dl.bounds.maxY += dy\n }\n\n console.log(`[钻孔对齐] ✓ ${dl.name} 已应用全局偏移`)\n }\n\n if (outlineBounds) {\n const afterBounds = translateBounds(combinedDrillBounds, dx, dy)\n const overflowAfter = computeOverflow(afterBounds, outlineBounds)\n console.log(`[钻孔对齐] 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)} (mm)`)\n }\n\n console.log('[钻孔对齐] 对齐检查完成')\n }\n\n // 先尝试:特征点匹配对齐(加入“多参考层尝试 + 平移投票”),并保留 GKO sanity check 防止推到板外\n let featureOffset = null\n\n // 选取锚点钻孔层:\n // - 优先使用“圆心/中心点”(type==='circle') 作为几何特征(DRL 通孔、焊盘开窗等)\n // - 避免 SLOT 层(通常是路径点/轮廓点),否则会把大量“唯一点”当特征导致误选锚点\n // - 同等情况下优先非 VIA 文件\n let anchorLayer = null\n let anchorFeatures = null\n let anchorCircleCount = 0\n let anchorScore = -Infinity\n let fallbackLayer = null\n let fallbackFeatures = null\n let fallbackScore = -Infinity\n\n for (const dl of drillLayers) {\n if (!dl.rawVertices) continue\n const featsAll = extractFeaturesFromLayer(dl)\n if (featsAll.length < 3) continue\n\n const nameUpper = (dl.name || '').toUpperCase()\n const viaPenalty = /VIA/.test(nameUpper) ? 100000 : 0\n const slotPenalty = dl.isSlotFile ? 50000 : 0\n\n const circleFeats = featsAll.filter(f => f && f.type === 'circle')\n const circleCount = circleFeats.length\n\n // 优先:能提取到足够多的 circle 特征(典型 DRL)\n if (circleCount >= 3) {\n const score = circleCount * 1000 - viaPenalty - slotPenalty\n if (score > anchorScore) {\n anchorScore = score\n anchorLayer = dl\n anchorFeatures = circleFeats\n anchorCircleCount = circleCount\n }\n } else {\n // 兜底:没有 circle 特征时才用“唯一点”作为特征(例如某些特殊 drill/slot)\n const score = featsAll.length - viaPenalty - slotPenalty\n if (score > fallbackScore) {\n fallbackScore = score\n fallbackLayer = dl\n fallbackFeatures = featsAll\n }\n }\n }\n\n if (!anchorLayer && fallbackLayer) {\n anchorLayer = fallbackLayer\n anchorFeatures = fallbackFeatures\n anchorCircleCount = (anchorFeatures || []).filter(f => f && f.type === 'circle').length\n }\n\n if (!anchorLayer || !anchorFeatures) {\n console.log('[钻孔对齐] ⚠️ 未找到可用的锚点钻孔层(特征点不足),跳过特征点对齐')\n } else {\n console.log(`[钻孔对齐] 选定锚点钻孔层: ${anchorLayer.name} (features=${anchorFeatures.length}, circle=${anchorCircleCount}, slot=${!!anchorLayer.isSlotFile})`)\n\n // 多参考层候选:优先铜层,再阻焊层(因为阻焊常包含大量SMT开窗,不一定对应钻孔)\n const nonDrillLayers = layers.filter(layer => !layer.isDrillFile && !layer.isSlotFile)\n const getLayerType = (name) => getLayerDisplayName(name)\n const refTypePriority = ['GTL', 'GBL', 'GTS', 'GBS']\n const refCandidates = []\n for (const t of refTypePriority) {\n for (const l of nonDrillLayers) {\n if (!l || !l.rawVertices) continue\n if (getLayerType(l.name) === t) refCandidates.push(l)\n }\n }\n\n if (refCandidates.length === 0) {\n console.log('[钻孔对齐] 未找到可用参考层(GTL/GBL/GTS/GBS 或缺少 rawVertices),跳过特征点对齐')\n } else {\n const selectedAnchorFeatures = selectFeaturesWithXYDistribution(anchorFeatures, Math.min(80, anchorFeatures.length))\n let best = null\n\n let secondBest = null\n for (const refLayer of refCandidates) {\n console.log(`[钻孔对齐] 尝试参考层: ${refLayer.name}`)\n const refAll = extractFeaturesFromLayer(refLayer)\n const refCircle = refAll.filter(f => f && f.type === 'circle')\n const refFeatures = refCircle.length >= 3 ? refCircle : refAll\n if (refFeatures.length < 3) {\n console.log(`[钻孔对齐] 参考层特征点不足(${refFeatures.length}个),跳过`)\n continue\n }\n\n const selectedRefFeatures = selectFeaturesWithXYDistribution(refFeatures, Math.min(80, refFeatures.length))\n\n // 方案1:平移投票(更鲁棒)\n const vote = estimateOffsetByVoting(selectedAnchorFeatures, selectedRefFeatures, { binSize: 0.2, maxCount: 80, minVotes: 3 })\n if (!vote) {\n console.log('[钻孔对齐] 平移投票未得到稳定候选')\n } else {\n // 用“紧容差”评估质量,避免参考层过密导致“任何偏移都能满匹配”的歧义\n const tolLoose = 0.6\n const tolRefine = 0.3\n const tolTight = 0.2\n\n // 先用中等容差做一次匹配,用匹配对细化 dx/dy(均值)\n const vRef = verifyOffset(anchorFeatures, refFeatures, vote.dx, vote.dy, tolRefine)\n if (vRef.matchCount >= 2) {\n let sumDx = 0, sumDy = 0\n for (const p of vRef.matchedPairs) {\n sumDx += (p.refPoint.x - p.drillPoint.x)\n sumDy += (p.refPoint.y - p.drillPoint.y)\n }\n const refinedDx = sumDx / vRef.matchedPairs.length\n const refinedDy = sumDy / vRef.matchedPairs.length\n\n const vTight = verifyOffset(anchorFeatures, refFeatures, refinedDx, refinedDy, tolTight)\n const vLoose = verifyOffset(anchorFeatures, refFeatures, refinedDx, refinedDy, tolLoose)\n\n const cand = {\n dx: refinedDx,\n dy: refinedDy,\n matchTight: vTight.matchCount,\n maxErrorTight: vTight.maxError,\n avgErrorTight: vTight.avgError,\n matchLoose: vLoose.matchCount,\n maxErrorLoose: vLoose.maxError,\n avgErrorLoose: vLoose.avgError,\n refType: getLayerType(refLayer.name),\n refTypeRank: ({ GTL: 0, GBL: 1, GTS: 2, GBS: 3 }[getLayerType(refLayer.name)] ?? 9),\n refLayerName: refLayer.name,\n method: `vote(bin=${vote.binSize}, votes=${vote.votes})`\n }\n\n const isBetter = (a, b) => {\n if (!b) return true\n // 先比紧容差下的真实对齐数量,再比误差\n if (a.matchTight !== b.matchTight) return a.matchTight > b.matchTight\n if (a.avgErrorTight !== b.avgErrorTight) return a.avgErrorTight < b.avgErrorTight\n if (a.matchLoose !== b.matchLoose) return a.matchLoose > b.matchLoose\n if (a.avgErrorLoose !== b.avgErrorLoose) return a.avgErrorLoose < b.avgErrorLoose\n // 最后用“参考层类型优先级”打破平局(优先铜层,其次阻焊)\n if ((a.refTypeRank ?? 9) !== (b.refTypeRank ?? 9)) return (a.refTypeRank ?? 9) < (b.refTypeRank ?? 9)\n // 再用“偏移量更小”做兜底(避免把板子不必要地移开)\n const sa = Math.abs(a.dx) + Math.abs(a.dy)\n const sb = Math.abs(b.dx) + Math.abs(b.dy)\n if (sa !== sb) return sa < sb\n return false\n }\n\n if (isBetter(cand, best)) {\n secondBest = best\n best = cand\n } else if (isBetter(cand, secondBest)) {\n secondBest = cand\n }\n\n console.log(`[钻孔对齐] 投票候选: dx=${cand.dx.toFixed(3)} dy=${cand.dy.toFixed(3)} tight=${cand.matchTight} loose=${cand.matchLoose} errT(avg/max)=${(cand.avgErrorTight ?? 0).toFixed(3)}/${(cand.maxErrorTight ?? 0).toFixed(3)} (${cand.method})`)\n } else {\n console.log(`[钻孔对齐] 投票候选匹配不足(matchRef=${vRef.matchCount}),忽略`)\n }\n }\n }\n\n // 接受偏移的阈值:\n // - 绝对匹配数要够(避免偶然匹配)\n // - 最佳候选要明显优于第二名(避免歧义)\n const totalAnchor = anchorFeatures?.length || 0\n const minMatches = totalAnchor <= 10\n ? 3\n : totalAnchor <= 50\n ? 5\n : Math.max(8, Math.ceil(totalAnchor * 0.02)) // 大钻孔文件:至少 2% 或至少 8 个点\n\n const bestTight = best?.matchTight ?? 0\n const secondTight = secondBest?.matchTight ?? 0\n\n // 用 tight 匹配数做主要分离;若 tight 打平,则用 tight 误差分离;仍打平则视为歧义\n const separationByCount = secondTight === 0 ? true : (bestTight >= secondTight + 3 || bestTight >= Math.ceil(secondTight * 1.5))\n let separationByError = true\n if (!separationByCount && secondBest && bestTight === secondTight && secondTight > 0) {\n const be = best?.avgErrorTight ?? Infinity\n const se = secondBest?.avgErrorTight ?? Infinity\n separationByError = (be + 1e-6) < se * 0.8 || (se - be) > 0.05\n }\n let separationOk = separationByCount || separationByError\n // 若 tight/误差都打平,但两个候选的偏移几乎一致,则不应视为“歧义”\n if (!separationOk && best && secondBest && bestTight === secondTight && secondTight > 0) {\n const delta = Math.hypot((best.dx ?? 0) - (secondBest.dx ?? 0), (best.dy ?? 0) - (secondBest.dy ?? 0))\n if (delta < 0.05) {\n separationOk = true\n console.log(`[钻孔对齐] ℹ️ tight 打平但偏移几乎一致(Δ=${delta.toFixed(3)}mm),视为可靠`)\n }\n }\n\n if (best && bestTight >= minMatches && separationOk) {\n featureOffset = { dx: best.dx, dy: best.dy, matchCount: bestTight, maxError: best.maxErrorTight, avgError: best.avgErrorTight, refLayerName: best.refLayerName }\n console.log(`[钻孔对齐] ✓ 选择最佳参考层: ${best.refLayerName}`)\n console.log(`[钻孔对齐] ✓ 特征点候选偏移: X=${featureOffset.dx.toFixed(3)}mm, Y=${featureOffset.dy.toFixed(3)}mm (tight=${bestTight}/${totalAnchor}, secondTight=${secondTight}, errT(avg/max)=${(best.avgErrorTight ?? 0).toFixed(3)}/${(best.maxErrorTight ?? 0).toFixed(3)}mm, method=${best.method})`)\n } else {\n const bestLoose = best?.matchLoose ?? 0\n console.log(`[钻孔对齐] ⚠️ 多参考层尝试后未达到可靠匹配阈值:bestTight=${bestTight}/${totalAnchor} (min=${minMatches}), secondTight=${secondTight}, separationOk=${separationOk}, bestLoose=${bestLoose},跳过特征点对齐`)\n }\n }\n }\n\n // 决策:优先用“特征点对齐”,但必须通过 GKO 越界校验;否则 fallback 到“按 GKO bounds 自动回拉”\n const applyThresholdMm = 0.2\n let chosen = null\n\n if (featureOffset) {\n const dx = featureOffset.dx\n const dy = featureOffset.dy\n\n // 如果偏移量极小就不动\n if (Math.abs(dx) < applyThresholdMm && Math.abs(dy) < applyThresholdMm) {\n // 重要:特征点匹配显示 drill 已与参考层对齐,此时不应再触发 GKO 兜底(否则会把 drill 又挪歪)\n console.log(`[钻孔对齐] ✓ 特征点匹配显示钻孔已对齐(Δ<${applyThresholdMm}mm, match=${featureOffset.matchCount}, avgErr=${(featureOffset.avgError ?? 0).toFixed(3)}). 跳过对齐与GKO兜底`)\n return\n } else if (outlineBounds && overflowBefore) {\n const afterBounds = translateBounds(combinedDrillBounds, dx, dy)\n const overflowAfter = computeOverflow(afterBounds, outlineBounds)\n const improvement = overflowBefore.total - overflowAfter.total\n // 只要能显著减少越界(或至少不恶化),就认为是合理偏移\n if (improvement > 0.2 || overflowAfter.total <= overflowBefore.total + 0.1) {\n chosen = { dx, dy, tag: `feature-match (improve=${improvement.toFixed(3)}mm)` }\n } else {\n console.warn(`[钻孔对齐] ⚠️ 特征点偏移会让钻孔更越界(before=${overflowBefore.total.toFixed(3)} after=${overflowAfter.total.toFixed(3)}),拒绝并启用GKO兜底`)\n }\n } else {\n // 没有GKO可校验,退回到特征点结果\n chosen = { dx, dy, tag: 'feature-match (no-outline-check)' }\n }\n }\n\n if (!chosen && outlineBounds && overflowBefore) {\n // 如果已经明显越界,按 GKO bounds 计算最优平移(小集合候选,取越界最小者)\n if (overflowBefore.total <= 0.2) {\n console.log('[钻孔对齐] ✓ 钻孔层未明显越界,无需GKO兜底对齐')\n } else {\n const drillCenterX = (combinedDrillBounds.minX + combinedDrillBounds.maxX) / 2\n const drillCenterY = (combinedDrillBounds.minY + combinedDrillBounds.maxY) / 2\n const outlineCenterX = (outlineBounds.minX + outlineBounds.maxX) / 2\n const outlineCenterY = (outlineBounds.minY + outlineBounds.maxY) / 2\n\n const candidatesX = [\n 0,\n outlineBounds.minX - combinedDrillBounds.minX,\n outlineBounds.maxX - combinedDrillBounds.maxX,\n outlineCenterX - drillCenterX\n ]\n const candidatesY = [\n 0,\n outlineBounds.minY - combinedDrillBounds.minY,\n outlineBounds.maxY - combinedDrillBounds.maxY,\n outlineCenterY - 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(combinedDrillBounds, dx, dy)\n const ov = computeOverflow(afterBounds, outlineBounds)\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\n const improvement = overflowBefore.total - best.error\n console.log(`[钻孔对齐] GKO兜底候选偏移: X=${best.dx.toFixed(3)}mm, Y=${best.dy.toFixed(3)}mm (overflowAfter=${best.error.toFixed(3)}mm, improve=${improvement.toFixed(3)}mm)`)\n\n if ((Math.abs(best.dx) >= applyThresholdMm || Math.abs(best.dy) >= applyThresholdMm) && (improvement > 0.2 || best.error <= overflowBefore.total + 0.1)) {\n chosen = { dx: best.dx, dy: best.dy, tag: `outline-fit (improve=${improvement.toFixed(3)}mm)` }\n } else {\n console.log('[钻孔对齐] ⚠️ GKO兜底偏移不显著/过小,跳过')\n }\n }\n }\n\n // 若没有可用 Outline(或被判为不可信),尝试用“参考层 bounds”做一次简单回拉(防止完全不对齐)\n if (!chosen && !outlineBounds) {\n const ref = pickReferenceForScale()\n if (ref && ref.bounds) {\n const overflowRefBefore = computeOverflow(combinedDrillBounds, ref.bounds)\n if (overflowRefBefore.total > 0.2) {\n const drillCenterX = (combinedDrillBounds.minX + combinedDrillBounds.maxX) / 2\n const drillCenterY = (combinedDrillBounds.minY + combinedDrillBounds.maxY) / 2\n const refCenterX = (ref.bounds.minX + ref.bounds.maxX) / 2\n const refCenterY = (ref.bounds.minY + ref.bounds.maxY) / 2\n\n const candidatesX = [\n 0,\n ref.bounds.minX - combinedDrillBounds.minX,\n ref.bounds.maxX - combinedDrillBounds.maxX,\n refCenterX - drillCenterX\n ]\n const candidatesY = [\n 0,\n ref.bounds.minY - combinedDrillBounds.minY,\n ref.bounds.maxY - combinedDrillBounds.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(combinedDrillBounds, dx, dy)\n const ov = computeOverflow(afterBounds, ref.bounds)\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\n const improvement = overflowRefBefore.total - best.error\n if ((Math.abs(best.dx) >= applyThresholdMm || Math.abs(best.dy) >= applyThresholdMm) && (improvement > 0.2 || best.error <= overflowRefBefore.total + 0.1)) {\n console.warn(`[钻孔对齐] ⚠️ 无Outline时参考层回拉: ref=${ref.name}(${ref.reason}), dx=${best.dx.toFixed(3)} dy=${best.dy.toFixed(3)} improve=${improvement.toFixed(3)}mm`)\n chosen = { dx: best.dx, dy: best.dy, tag: `ref-fit (ref=${ref.reason}, improve=${improvement.toFixed(3)}mm)` }\n }\n }\n }\n }\n\n if (!chosen) {\n console.log('[钻孔对齐] 未选择任何有效偏移,跳过')\n return\n }\n\n applyOffsetToAllDrills(chosen.dx, chosen.dy, chosen.tag)\n}\n\n/**\n * 从图层的vertices数据中提取特征点(圆形孔的中心点)\n */\nfunction extractFeaturesFromLayer(layer) {\n if (!layer.rawVertices) return []\n \n const features = []\n const vertices = layer.rawVertices\n \n // 钻孔文件通常是圆形,每个圆由一个中心点+多个周边点组成\n // 我们通过检测顶点簇来识别圆心\n const centerCandidates = new Map() // key: \"x,y\", value: count\n \n for (let i = 0; i < vertices.length; i += 3) {\n const x = vertices[i]\n const y = vertices[i + 1]\n \n // 四舍五入到0.01mm精度,避免浮点误差\n const key = `${Math.round(x * 100) / 100},${Math.round(y * 100) / 100}`\n centerCandidates.set(key, (centerCandidates.get(key) || 0) + 1)\n }\n \n // 找出重复出现的点(这些可能是圆心)\n // 圆心应该重复至少10次(我们额外添加了10个中心点)\n const circleThreshold = 8 // 降低到8以应对可能的精度损失\n centerCandidates.forEach((count, key) => {\n if (count >= circleThreshold) {\n const [x, y] = key.split(',').map(Number)\n features.push({ x, y, type: 'circle', count: count })\n }\n })\n \n console.log(`[提取特征点] ${layer.name} - 找到 ${features.length} 个圆心候选点 (阈值=${circleThreshold})`)\n \n // 显示前5个圆心\n if (features.length > 0) {\n features.slice(0, 5).forEach((f, idx) => {\n console.log(` 圆心${idx + 1}: (${f.x.toFixed(3)}, ${f.y.toFixed(3)}), 重复次数=${f.count}`)\n })\n }\n \n // 如果没有找到重复点,尝试提取所有唯一点\n if (features.length === 0) {\n console.log(`[提取特征点] ${layer.name} - 未找到圆心,尝试提取唯一点`)\n const uniquePoints = new Set()\n for (let i = 0; i < vertices.length; i += 3) {\n const x = Math.round(vertices[i] * 100) / 100\n const y = Math.round(vertices[i + 1] * 100) / 100\n uniquePoints.add(`${x},${y}`)\n }\n \n uniquePoints.forEach(key => {\n const [x, y] = key.split(',').map(Number)\n features.push({ x, y, type: 'point' })\n })\n console.log(`[提取特征点] ${layer.name} - 提取到 ${features.length} 个唯一点`)\n }\n \n return features\n}\n\n/**\n * 选择特征点,优先选择四个角上的特征点(从2d版本移植)\n */\nfunction selectFeaturesWithXYDistribution(features, maxCount) {\n if (features.length <= maxCount) {\n return features\n }\n \n // 计算X和Y坐标的范围\n let minX = Infinity\n let maxX = -Infinity\n let minY = Infinity\n let maxY = -Infinity\n \n for (const feature of features) {\n minX = Math.min(minX, feature.x)\n maxX = Math.max(maxX, feature.x)\n minY = Math.min(minY, feature.y)\n maxY = Math.max(maxY, feature.y)\n }\n \n const rangeX = maxX - minX\n const rangeY = maxY - minY\n \n // 如果范围太小,使用简单的排序方法\n if (rangeX < 0.001 || rangeY < 0.001) {\n features.sort((a, b) => {\n if (Math.abs(a.y - b.y) > 0.0001) {\n return a.y - b.y\n }\n return a.x - b.x\n })\n return features.slice(0, maxCount)\n }\n \n const selectedFeatures = []\n const usedFeatures = new Set()\n \n // 定义四个角的区域(每个区域占整个范围的1/3)\n const cornerRegionSize = 0.33\n \n const corners = [\n { name: '左下角', xMin: minX, xMax: minX + rangeX * cornerRegionSize, yMin: minY, yMax: minY + rangeY * cornerRegionSize },\n { name: '右下角', xMin: maxX - rangeX * cornerRegionSize, xMax: maxX, yMin: minY, yMax: minY + rangeY * cornerRegionSize },\n { name: '左上角', xMin: minX, xMax: minX + rangeX * cornerRegionSize, yMin: maxY - rangeY * cornerRegionSize, yMax: maxY },\n { name: '右上角', xMin: maxX - rangeX * cornerRegionSize, xMax: maxX, yMin: maxY - rangeY * cornerRegionSize, yMax: maxY }\n ]\n \n // 第一步:从四个角各选择1个最接近角落的特征点\n for (const corner of corners) {\n const cornerFeatures = features.filter(f => \n f.x >= corner.xMin && f.x <= corner.xMax &&\n f.y >= corner.yMin && f.y <= corner.yMax &&\n !usedFeatures.has(f)\n )\n \n if (cornerFeatures.length > 0) {\n // 计算角落的极值点\n let cornerCenterX, cornerCenterY\n if (corner.name === '左下角') {\n cornerCenterX = minX\n cornerCenterY = minY\n } else if (corner.name === '右下角') {\n cornerCenterX = maxX\n cornerCenterY = minY\n } else if (corner.name === '左上角') {\n cornerCenterX = minX\n cornerCenterY = maxY\n } else {\n cornerCenterX = maxX\n cornerCenterY = maxY\n }\n \n // 优先选择圆形\n const circleFeatures = cornerFeatures.filter(f => f.type === 'circle')\n \n // 选择距离角落最近的特征点\n let closestFeature = null\n let minDistance = Infinity\n \n const featuresToCheck = circleFeatures.length > 0 ? circleFeatures : cornerFeatures\n for (const feature of featuresToCheck) {\n const distance = Math.sqrt(\n Math.pow(feature.x - cornerCenterX, 2) + Math.pow(feature.y - cornerCenterY, 2)\n )\n if (distance < minDistance) {\n minDistance = distance\n closestFeature = feature\n }\n }\n \n if (closestFeature) {\n selectedFeatures.push(closestFeature)\n usedFeatures.add(closestFeature)\n }\n }\n }\n \n console.log(`[特征点选择] 四个角共选择 ${selectedFeatures.length} 个特征点`)\n \n // 第二步:如果不足maxCount,从剩余区域补充\n if (selectedFeatures.length < maxCount) {\n const remainingFeatures = features.filter(f => !usedFeatures.has(f))\n \n if (remainingFeatures.length > 0) {\n // 使用网格方法从剩余区域选择\n const gridSize = Math.ceil(Math.sqrt(maxCount - selectedFeatures.length))\n const cellWidth = rangeX / gridSize\n const cellHeight = rangeY / gridSize\n \n const grid = Array(gridSize).fill(null).map(() => Array(gridSize).fill(null).map(() => []))\n \n for (const feature of remainingFeatures) {\n const gridX = Math.min(Math.floor((feature.x - minX) / cellWidth), gridSize - 1)\n const gridY = Math.min(Math.floor((feature.y - minY) / cellHeight), gridSize - 1)\n grid[gridY][gridX].push(feature)\n }\n \n for (let y = 0; y < gridSize; y++) {\n for (let x = 0; x < gridSize; x++) {\n const cellFeatures = grid[y][x]\n if (cellFeatures.length > 0) {\n const cellCenterX = minX + (x + 0.5) * cellWidth\n const cellCenterY = minY + (y + 0.5) * cellHeight\n \n let closestFeature = cellFeatures[0]\n let minDistance = Infinity\n \n for (const feature of cellFeatures) {\n const distance = Math.sqrt(\n Math.pow(feature.x - cellCenterX, 2) + Math.pow(feature.y - cellCenterY, 2)\n )\n if (distance < minDistance) {\n minDistance = distance\n closestFeature = feature\n }\n }\n \n selectedFeatures.push(closestFeature)\n usedFeatures.add(closestFeature)\n \n if (selectedFeatures.length >= maxCount) {\n break\n }\n }\n }\n if (selectedFeatures.length >= maxCount) {\n break\n }\n }\n \n console.log(`[特征点选择] 从剩余区域补充 ${selectedFeatures.length - (selectedFeatures.length > 4 ? 4 : selectedFeatures.length)} 个特征点`)\n }\n }\n \n console.log(`[特征点选择] 最终从 ${features.length} 个特征点中选择 ${selectedFeatures.length} 个`)\n \n return selectedFeatures\n}\n\n/**\n * 计算特征点之间的相对距离矩阵\n */\nfunction calculateRelativeDistances(features) {\n const distances = []\n \n for (let i = 0; i < features.length; i++) {\n for (let j = i + 1; j < features.length; j++) {\n const dx = features[j].x - features[i].x\n const dy = features[j].y - features[i].y\n const distance = Math.sqrt(dx * dx + dy * dy)\n \n distances.push({\n i,\n j,\n distance,\n dx,\n dy\n })\n }\n }\n \n // 按距离排序\n distances.sort((a, b) => a.distance - b.distance)\n \n return distances\n}\n\n/**\n * 基于相对距离匹配特征点(从2d版本移植)\n */\nfunction matchFeaturesByRelativeDistances(drillFeatures, refFeatures, drillDistances, refDistances) {\n const tolerance = 0.5 // 距离容差:0.5mm(从0.01英寸转换,约等于0.254mm,放宽到0.5mm)\n \n console.log(`[钻孔对齐] 开始匹配,钻孔层 ${drillFeatures.length} 个点,参考层 ${refFeatures.length} 个点`)\n \n let bestOverallMatch = null\n let bestMatchCount = 0\n \n // 尝试前10个最短距离\n const minDistancesToMatch = Math.min(10, drillDistances.length, refDistances.length)\n const drillShortestDistances = drillDistances.slice(0, minDistancesToMatch)\n \n for (const drillDist of drillShortestDistances) {\n // 在参考层中查找与钻孔层距离匹配的点对(容差范围内)\n for (const refDist of refDistances) {\n const distDiff = Math.abs(refDist.distance - drillDist.distance)\n \n if (distDiff <= tolerance) {\n // 找到匹配距离,计算两种可能的偏移量\n const drillPoint1 = drillFeatures[drillDist.i]\n const drillPoint2 = drillFeatures[drillDist.j]\n const refPoint1 = refFeatures[refDist.i]\n const refPoint2 = refFeatures[refDist.j]\n \n // 可能性1:drillPoint1 对应 refPoint1\n const offsetX1 = refPoint1.x - drillPoint1.x\n const offsetY1 = refPoint1.y - drillPoint1.y\n \n // 可能性2:drillPoint1 对应 refPoint2 \n const offsetX2 = refPoint2.x - drillPoint1.x\n const offsetY2 = refPoint2.y - drillPoint1.y\n \n // 验证哪种偏移量能匹配更多点\n const match1 = verifyOffset(drillFeatures, refFeatures, offsetX1, offsetY1, tolerance)\n const match2 = verifyOffset(drillFeatures, refFeatures, offsetX2, offsetY2, tolerance)\n \n const bestMatch = match1.matchCount >= match2.matchCount ? \n { offsetX: offsetX1, offsetY: offsetY1, ...match1 } :\n { offsetX: offsetX2, offsetY: offsetY2, ...match2 }\n \n // 更新最佳匹配\n if (bestMatch.matchCount > bestMatchCount) {\n bestMatchCount = bestMatch.matchCount\n bestOverallMatch = bestMatch\n console.log(`[钻孔对齐] 找到更好的匹配:${bestMatch.matchCount}/${drillFeatures.length} 个点,偏移量 (${bestMatch.offsetX.toFixed(3)}, ${bestMatch.offsetY.toFixed(3)})`)\n }\n \n // 如果匹配率 >= 80%,提前退出\n const matchRate = bestMatch.matchCount / drillFeatures.length\n if (matchRate >= 0.8) {\n console.log(`[钻孔对齐] ✓ 匹配率很高 (${(matchRate * 100).toFixed(1)}%)`)\n return { ...bestOverallMatch, success: true }\n }\n }\n }\n }\n \n // 如果找到任何匹配(≥2个点),返回最佳匹配\n if (bestOverallMatch && bestOverallMatch.matchCount >= 2) {\n const matchRate = bestOverallMatch.matchCount / drillFeatures.length\n console.log(`[钻孔对齐] ✓ 找到匹配:${bestOverallMatch.matchCount}/${drillFeatures.length} 个点 (${(matchRate * 100).toFixed(1)}%)`)\n return { ...bestOverallMatch, success: true }\n }\n \n console.log(`[钻孔对齐] ✗ 未找到足够的匹配点`)\n return { success: false }\n}\n\n/**\n * 验证给定偏移量能匹配多少个点\n */\nfunction verifyOffset(drillFeatures, refFeatures, offsetX, offsetY, tolerance) {\n let matchCount = 0\n let maxError = 0\n let sumError = 0\n const matchedPairs = []\n const usedRefIndices = new Set()\n \n // 对每个钻孔层点,应用偏移后查找最近的未使用的参考层点\n for (const drillPoint of drillFeatures) {\n const transformedX = drillPoint.x + offsetX\n const transformedY = drillPoint.y + offsetY\n \n // 查找最近的未使用的参考点\n let minDistance = Infinity\n let nearestRefPoint = null\n let nearestRefIndex = -1\n \n for (let i = 0; i < refFeatures.length; i++) {\n if (usedRefIndices.has(i)) continue\n \n const refPoint = refFeatures[i]\n const distance = Math.sqrt(\n Math.pow(transformedX - refPoint.x, 2) +\n Math.pow(transformedY - refPoint.y, 2)\n )\n \n if (distance < minDistance) {\n minDistance = distance\n nearestRefPoint = refPoint\n nearestRefIndex = i\n }\n }\n \n // 如果距离在容差范围内,认为匹配\n if (minDistance <= tolerance && nearestRefIndex !== -1) {\n matchCount++\n maxError = Math.max(maxError, minDistance)\n sumError += minDistance\n usedRefIndices.add(nearestRefIndex)\n matchedPairs.push({\n drillPoint: drillPoint,\n refPoint: nearestRefPoint,\n error: minDistance\n })\n }\n }\n \n const avgError = matchCount > 0 ? (sumError / matchCount) : Infinity\n return { matchCount, maxError, avgError, matchedPairs }\n}\n\n/**\n * 基于“平移投票”的偏移量估计:\n * 从 drill/ref 特征点子集中枚举(dx,dy)=ref-drill,按网格量化统计最高票的平移。\n * 这种方法对“参考层包含大量非钻孔圆焊盘(SMT)”更鲁棒(正确平移会形成明显的票数簇)。\n */\nfunction estimateOffsetByVoting(drillFeatures, refFeatures, options = {}) {\n const binSize = options.binSize ?? 0.2 // mm:量化网格\n const maxCount = options.maxCount ?? 80 // 参与投票的点数上限(防止 O(n^2) 爆炸)\n const minVotes = options.minVotes ?? 3\n\n if (!Array.isArray(drillFeatures) || !Array.isArray(refFeatures)) return null\n if (drillFeatures.length === 0 || refFeatures.length === 0) return null\n\n const drillSample = selectFeaturesWithXYDistribution(drillFeatures, Math.min(maxCount, drillFeatures.length))\n const refSample = selectFeaturesWithXYDistribution(refFeatures, Math.min(maxCount, refFeatures.length))\n\n const bins = new Map() // key => {count,sumDx,sumDy}\n for (const d of drillSample) {\n for (const r of refSample) {\n const dx = r.x - d.x\n const dy = r.y - d.y\n const qx = Math.round(dx / binSize)\n const qy = Math.round(dy / binSize)\n const key = `${qx},${qy}`\n const cur = bins.get(key) || { count: 0, sumDx: 0, sumDy: 0 }\n cur.count += 1\n cur.sumDx += dx\n cur.sumDy += dy\n bins.set(key, cur)\n }\n }\n\n let bestKey = null\n let best = null\n for (const [k, v] of bins.entries()) {\n if (!best || v.count > best.count) {\n best = v\n bestKey = k\n }\n }\n\n if (!best || best.count < minVotes) return null\n const dx = best.sumDx / best.count\n const dy = best.sumDy / best.count\n return { dx, dy, votes: best.count, binSize, key: bestKey }\n}\n\n/**\n * 查找参考层(优先级:GTS → GBS → GTL → GBL)\n */\nfunction findReferenceLayer() {\n // 提取图层类型\n const getLayerType = (name) => {\n const fileNameOnly = name.split(/[/\\\\]/).pop()\n const ext = '.' + fileNameOnly.split('.').pop().toUpperCase()\n return ext.substring(1)\n }\n \n // 只使用标准Gerber层作为参考(排除钻孔层)\n const nonDrillLayers = layers.filter(layer => !layer.isDrillFile && !layer.isSlotFile)\n \n if (nonDrillLayers.length === 0) {\n console.log('[钻孔对齐] 未找到非钻孔层')\n return null\n }\n \n // 按优先级查找:GTS → GBS → GTL → GBL\n const priorities = ['GTS', 'GBS', 'GTL', 'GBL']\n \n for (const type of priorities) {\n const layer = nonDrillLayers.find(l => getLayerType(l.name) === type && l.rawVertices)\n if (layer) {\n console.log(`[钻孔对齐] 使用参考层: ${layer.name} (${type})`)\n return layer\n }\n }\n \n console.log('[钻孔对齐] 未找到合适的参考层(GTS/GBS/GTL/GBL)')\n return null\n}\n\nfunction centerView() {\n // 优先使用GKO或GTO层的bounds来设置视图,避免偏移的钻孔层影响视图范围\n // 但如果GKO层很小,会同时考虑GTL层的边界,取最大值\n let viewBounds = null\n let useLargePadding = false // 标记是否使用大边距(40%)\n \n // 1. 查找GKO层和GTL层\n const gkoLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const display = getLayerDisplayName(fileName)\n return display === 'GKO' ||\n ext === 'GKO' || \n fileNameOnly.includes('_Profile') ||\n fileNameOnly.toLowerCase().includes('outline')\n })\n \n const gtlLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const display = getLayerDisplayName(fileName)\n return display === 'GTL' ||\n ext === 'GTL' || \n fileNameOnly.includes('_TOP') ||\n fileNameOnly.includes('_Copper_Signal_Top')\n })\n \n // 如果同时有GKO和GTL层,默认合并边界;但当 GKO 明显异常(尺寸远大于 GTL)时避免被撑大\n if (gkoLayer && gkoLayer.bounds && gtlLayer && gtlLayer.bounds) {\n const gkoBounds = gkoLayer.bounds\n const gtlBounds = gtlLayer.bounds\n \n // 计算边界大小\n const gkoWidth = gkoBounds.maxX - gkoBounds.minX\n const gkoHeight = gkoBounds.maxY - gkoBounds.minY\n const gkoSize = Math.max(gkoWidth, gkoHeight)\n \n const gtlWidth = gtlBounds.maxX - gtlBounds.minX\n const gtlHeight = gtlBounds.maxY - gtlBounds.minY\n const gtlSize = Math.max(gtlWidth, gtlHeight)\n \n const ratio = gtlSize > 0 ? (gkoSize / gtlSize) : Infinity\n console.log(`[视图居中] GKO层大小: ${gkoSize.toFixed(6)}, GTL层大小: ${gtlSize.toFixed(6)}`)\n\n if (Number.isFinite(ratio) && ratio > 5) {\n // GKO 明显异常:优先以 GTL 为视图基准(避免视图被撑大)\n viewBounds = { ...gtlBounds }\n console.warn(`[视图居中] ⚠️ GKO尺寸远大于GTL (ratio=${ratio.toFixed(2)} > 5),为避免视图被异常撑大,改为使用GTL层bounds设置视图`)\n console.log(`[视图居中] GTL: minX=${gtlBounds.minX.toFixed(6)}, minY=${gtlBounds.minY.toFixed(6)}, maxX=${gtlBounds.maxX.toFixed(6)}, maxY=${gtlBounds.maxY.toFixed(6)}`)\n } else {\n // 正常情况:合并边界(确保包含轮廓+铜层)\n viewBounds = {\n minX: Math.min(gkoBounds.minX, gtlBounds.minX),\n minY: Math.min(gkoBounds.minY, gtlBounds.minY),\n maxX: Math.max(gkoBounds.maxX, gtlBounds.maxX),\n maxY: Math.max(gkoBounds.maxY, gtlBounds.maxY)\n }\n console.log(`[视图居中] 使用GKO和GTL层的合并边界设置视图`)\n console.log(`[视图居中] GKO: minX=${gkoBounds.minX.toFixed(6)}, minY=${gkoBounds.minY.toFixed(6)}, maxX=${gkoBounds.maxX.toFixed(6)}, maxY=${gkoBounds.maxY.toFixed(6)}`)\n console.log(`[视图居中] GTL: minX=${gtlBounds.minX.toFixed(6)}, minY=${gtlBounds.minY.toFixed(6)}, maxX=${gtlBounds.maxX.toFixed(6)}, maxY=${gtlBounds.maxY.toFixed(6)}`)\n console.log(`[视图居中] 合并: minX=${viewBounds.minX.toFixed(6)}, minY=${viewBounds.minY.toFixed(6)}, maxX=${viewBounds.maxX.toFixed(6)}, maxY=${viewBounds.maxY.toFixed(6)}`)\n }\n } else if (gkoLayer && gkoLayer.bounds) {\n // 只有GKO层\n viewBounds = { ...gkoLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用GKO层 (${gkoLayer.name}) 的bounds设置视图`)\n } else if (gtlLayer && gtlLayer.bounds) {\n // 只有GTL层\n viewBounds = { ...gtlLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用GTL层 (${gtlLayer.name}) 的bounds设置视图`)\n } else {\n // 3. 如果没有GKO和GTL层,查找GTO层\n const gtoLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n const fileExtension = '.' + fileNameOnly.split('.').pop().toUpperCase()\n const ext = fileExtension.substring(1)\n const display = getLayerDisplayName(fileName)\n return display === 'GTO' ||\n ext === 'GTO' || \n fileNameOnly.includes('_Legend_Top') ||\n (fileNameOnly.toLowerCase().includes('legend') && fileNameOnly.toLowerCase().includes('top')) ||\n (fileNameOnly.toLowerCase().includes('silkscreen') && fileNameOnly.toLowerCase().includes('top'))\n })\n \n if (gtoLayer && gtoLayer.bounds) {\n viewBounds = { ...gtoLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用GTO层 (${gtoLayer.name}) 的bounds设置视图`)\n } else {\n // 4. 如果没有GKO/GTL/GTO层,查找以art开头的文件作为轮廓层\n const artLayer = layers.find(layer => {\n const fileName = layer.name\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\n return fileNameOnly.toLowerCase().startsWith('art')\n })\n \n if (artLayer && artLayer.bounds) {\n viewBounds = { ...artLayer.bounds } // 创建副本\n console.log(`[视图居中] 使用art开头文件 (${artLayer.name}) 的bounds设置视图`)\n } else {\n // 5. 如果都没有,使用所有图层的边界(使用大边距)\n let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity\n layers.forEach(layer => {\n if (layer.bounds) {\n minX = Math.min(minX, layer.bounds.minX)\n maxX = Math.max(maxX, layer.bounds.maxX)\n minY = Math.min(minY, layer.bounds.minY)\n maxY = Math.max(maxY, layer.bounds.maxY)\n }\n })\n viewBounds = { minX, maxX, minY, maxY }\n useLargePadding = true\n console.log(`[视图居中] 未找到GKO/GTL/GTO/art开头文件,使用所有图层边界(大边距)`)\n }\n }\n }\n \n if (!viewBounds) return\n \n const width = viewBounds.maxX - viewBounds.minX\n const height = viewBounds.maxY - viewBounds.minY\n \n if (width <= 0 || height <= 0) {\n console.warn(`[视图居中] 无效边界: width=${width}, height=${height}`)\n return\n }\n \n // 根据是否使用大边距决定padding大小\n // 与 2d 页面保持一致:如果useLargePadding为true,使用40%边距;否则使用10%边距\n const paddingRatio = useLargePadding ? 0.40 : 0.1\n const padding = Math.max(width, height) * paddingRatio\n const paddedWidth = width + padding * 2\n const paddedHeight = height + padding * 2\n \n const cx = (viewBounds.minX + viewBounds.maxX) / 2\n const cy = (viewBounds.minY + viewBounds.maxY) / 2\n \n const aspect = canvas.width / canvas.height\n const contentAspect = paddedWidth / paddedHeight\n \n // 计算缩放比例,使内容适应画布\n // 使用 1.0 倍,让内容显示稍大一些(比 2d 页面的 0.9 倍稍大)\n if (contentAspect > aspect) {\n // 内容更宽,以宽度为准\n scale = 2.0 / paddedWidth * 1.4\n } else {\n // 内容更高,以高度为准\n scale = (2.0 / paddedHeight) / aspect * 1.4\n }\n \n transX = -cx * (scale / aspect)\n transY = -cy * scale\n \n console.log(`[视图居中] 边界: [${viewBounds.minX.toFixed(2)}, ${viewBounds.maxX.toFixed(2)}] x [${viewBounds.minY.toFixed(2)}, ${viewBounds.maxY.toFixed(2)}]`)\n console.log(`[视图居中] 缩放: ${scale.toFixed(6)}, 平移: (${transX.toFixed(6)}, ${transY.toFixed(6)})`)\n}\n\n// --- 初始化 URL ID 处理 ---\n// 创建一个包装函数来处理文件选择(适配 2d-direct 的文件处理逻辑)\nasync function handleFileSelectionForDirect(files) {\n if (files.length === 0) return\n \n const file = files[0]\n if (!file) return\n \n // 触发 fileInput 的 change 事件,使用 2d-direct 现有的文件处理逻辑\n // 创建一个新的 FileList 对象\n const dataTransfer = new DataTransfer()\n dataTransfer.items.add(file)\n fileInput.files = dataTransfer.files\n \n // 触发 change 事件\n const event = new Event('change', { bubbles: true })\n fileInput.dispatchEvent(event)\n}\n\n// 页面加载完成后初始化\nwindow.addEventListener('load', () => {\n // 初始化 URL ID 处理(如果 URL 中有 id,自动获取并处理文件)\n initUrlIdHandler(handleFileSelectionForDirect, showStatus)\n\n // 嵌入 @yumiai/chat-widget 时无独立 Vite BASE_URL;避免 tsup CJS 对 import.meta 告警\n const baseUrl = '/'\n const buildPageUrl = (path) => `${baseUrl}${path.replace(/^\\/+/, '')}`\n const getTargetUrl = (basePath) => {\n const targetBase = buildPageUrl(basePath)\n const id = parseUrlId()\n if (id) return `${targetBase}?id=${encodeURIComponent(id)}`\n const url = parseUrlFileUrl()\n if (url) return `${targetBase}?url=${encodeURIComponent(url)}`\n return targetBase\n }\n \n // 绑定切换模式按钮(跳转回标准模式页面)\n // const toggleModeBtn = document.querySelector('.viewer-action-btn:nth-child(1)')\n // if (toggleModeBtn) {\n // toggleModeBtn.addEventListener('click', () => {\n // // 从当前 URL 中提取 ID(支持路径格式和查询参数格式)\n // const id = parseUrlId()\n \n // if (id) {\n // // 使用新的路径格式:/2d/{id}\n // const newUrl = new URL(`../2d/${id}`, window.location.origin)\n // window.location.href = newUrl.toString()\n // } else {\n // // 如果没有 ID,跳转到默认页面\n // window.location.href = '../2d/index.html'\n // }\n // })\n // }\n \n // 绑定 3D 模式按钮\n const mode3DBtn = document.querySelector('#mode3DBtnId')\n if (mode3DBtn) {\n mode3DBtn.addEventListener('click', () => {\n window.location.href = getTargetUrl('/3dPage')\n })\n }\n\n // 绑定仿真图按钮\n const modeSimulationBtn = document.querySelector('#modeSimulationBtnId')\n if (modeSimulationBtn) {\n modeSimulationBtn.addEventListener('click', () => {\n window.location.href = getTargetUrl('/simulation')\n })\n }\n})\n\n"],"mappings":";;;;;;;;AAIA,OAAO,YAAY;AAEZ,IAAM,qBAAN,MAAyB;AAAA,EAC9B,YAAY,UAAU,CAAC,GAAG;AACxB,SAAK,WAAW,CAAC;AACjB,SAAK,SAAS,CAAC;AACf,SAAK,UAAU,CAAC;AAChB,SAAK,kBAAkB,CAAC;AACxB,SAAK,WAAW,CAAC;AAGjB,SAAK,eAAe,QAAQ,gBAAgB;AAG5C,SAAK,QAAQ;AAAA,MACX,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA,MACH,GAAG;AAAA;AAAA,MACH,GAAG;AAAA;AAAA,MACH,UAAU;AAAA;AAAA,MACV,eAAe;AAAA;AAAA,MACf,YAAY;AAAA;AAAA,MACZ,UAAU;AAAA;AAAA,MACV,MAAM;AAAA;AAAA,MACN,UAAU;AAAA;AAAA,MACV,QAAQ;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,MAAM;AAAA;AAAA,MACR;AAAA,IACF;AAIA,SAAK,eAAe,CAAC,EAAE,UAAU,KAAK,MAAM,UAAU,OAAO,GAAG,KAAK,EAAE,CAAC;AAGxE,SAAK,YAAY,CAAC;AAElB,SAAK,SAAS,CAAC;AAIf,SAAK,wBAAwB,CAAC;AAC9B,SAAK,wBAAwB;AAAA,EAC/B;AAAA,EAEA,MAAM,SAAS;AACb,UAAM,QAAQ,QAAQ,MAAM,SAAS;AAErC,aAAS,QAAQ,OAAO;AACtB,aAAO,KAAK,KAAK;AACjB,UAAI,CAAC,KAAM;AAGX,UAAI,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAAG;AAC9C,aAAK,qBAAqB,KAAK,UAAU,GAAG,KAAK,SAAS,CAAC,CAAC;AAC5D;AAAA,MACF;AAIA,YAAM,WAAW,KAAK,MAAM,GAAG;AAC/B,eAAS,OAAO,UAAU;AACxB,cAAM,IAAI,KAAK;AACf,YAAI,CAAC,IAAK;AACV,aAAK,aAAa,GAAG;AAAA,MACvB;AAAA,IACF;AAGA,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,WAAK,aAAa,KAAK,aAAa,SAAS,CAAC,EAAE,MAAM,KAAK,gBAAgB;AAAA,IAC7E;AAEA,WAAO;AAAA,MACL,UAAU,IAAI,aAAa,KAAK,QAAQ;AAAA,MACxC,QAAQ,IAAI,aAAa,KAAK,MAAM;AAAA,MACpC,iBAAiB,IAAI,YAAY,KAAK,eAAe;AAAA;AAAA,MACrD,UAAU,KAAK;AAAA;AAAA;AAAA,MAEf,cAAc,KAAK,aAAa,OAAO,OAAM,EAAE,MAAM,EAAE,QAAS,CAAC;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,qBAAqB,KAAK;AAExB,QAAI,IAAI,WAAW,IAAI,GAAG;AAExB,YAAM,QAAQ,IAAI,MAAM,iCAAiC;AACzD,UAAI,OAAO;AACT,aAAK,MAAM,OAAO,OAAO,MAAM,CAAC,KAAK;AACrC,aAAK,MAAM,OAAO,UAAU,SAAS,MAAM,CAAC,CAAC;AAC7C,aAAK,MAAM,OAAO,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,MAC/C;AAAA,IACF,WAES,IAAI,WAAW,IAAI,GAAG;AAC7B,WAAK,MAAM,OAAO,IAAI,SAAS,IAAI,IAAI,SAAS;AAAA,IAClD,WAES,IAAI,WAAW,IAAI,GAAG;AAE7B,YAAM,QAAQ,IAAI,MAAM,2BAA2B;AACnD,UAAI,OAAO;AACT,cAAM,QAAQ,SAAS,MAAM,CAAC,CAAC;AAC/B,cAAM,OAAO,MAAM,CAAC;AACpB,cAAM,SAAS,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE,IAAI,OAAK,WAAW,CAAC,CAAC;AACzD,aAAK,UAAU,KAAK,IAAI,EAAE,MAAM,OAAO;AAAA,MACzC;AAAA,IACF,WAES,IAAI,WAAW,IAAI,GAAG;AAC7B,UAAI,IAAI,WAAW,KAAK,EAAG,MAAK,gBAAgB,MAAM;AAAA,eAC7C,IAAI,WAAW,KAAK,EAAG,MAAK,gBAAgB,OAAO;AAAA,IAC9D;AAAA,EAIF;AAAA,EAEA,gBAAgB,cAAc;AAC5B,QAAI,CAAC,gBAAgB,KAAK,MAAM,aAAa,aAAc;AAG3D,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,WAAK,aAAa,KAAK,aAAa,SAAS,CAAC,EAAE,MAAM,KAAK,gBAAgB;AAAA,IAC7E;AAEA,SAAK,MAAM,WAAW;AAGtB,UAAM,QAAQ,KAAK,gBAAgB;AACnC,SAAK,aAAa,KAAK,EAAE,UAAU,cAAc,OAAO,KAAK,MAAM,CAAC;AAAA,EACtE;AAAA,EAEA,aAAa,KAAK;AAEhB,QAAI,IAAI,WAAW,GAAG,GAAG;AACvB,YAAM,QAAQ,SAAS,IAAI,UAAU,GAAG,CAAC,CAAC;AAI1C,UAAI,UAAU,IAAI;AAChB,cAAM,OAAO,IAAI,MAAM,QAAQ;AAC/B,YAAI,MAAM;AACR,gBAAM,QAAQ,SAAS,KAAK,CAAC,GAAG,EAAE;AAClC,cAAI,SAAS,IAAI;AACf,iBAAK,MAAM,WAAW;AACtB;AAAA,UACF;AAAA,QACF;AAEA;AAAA,MACF;AACA,UAAI,UAAU,EAAG,MAAK,MAAM,gBAAgB;AAC5C,UAAI,UAAU,EAAG,MAAK,MAAM,gBAAgB;AAC5C,UAAI,UAAU,EAAG,MAAK,MAAM,gBAAgB;AAC5C,UAAI,UAAU,IAAI;AAChB,aAAK,MAAM,aAAa;AACxB,aAAK,wBAAwB,CAAC;AAC9B,aAAK,wBAAwB;AAAA,MAC/B;AACA,UAAI,UAAU,IAAI;AAChB,aAAK,MAAM,aAAa;AACxB,aAAK,aAAa;AAAA,MACpB;AAGA,UAAI,IAAI,SAAS,GAAG;AAClB,aAAK,gBAAgB,IAAI,UAAU,CAAC,CAAC;AAAA,MACvC;AACA;AAAA,IACF;AAGA,QAAI,IAAI,WAAW,GAAG,GAAG;AACvB,YAAM,QAAQ,SAAS,IAAI,UAAU,CAAC,CAAC;AAEvC,UAAI,SAAS,IAAI;AACf,aAAK,MAAM,WAAW;AACtB;AAAA,MACF;AAGA,UAAI,UAAU,KAAK,UAAU,KAAK,UAAU,GAAG;AAE7C,cAAM,IAAI,KAAK,MAAM;AACrB,cAAM,IAAI,KAAK,MAAM;AAErB,YAAI,UAAU,GAAG;AAEf,cAAI,KAAK,MAAM,UAAU;AAAA,UAGzB;AAAA,QACF,WAAW,UAAU,GAAG;AAAA,QAExB,WAAW,UAAU,GAAG;AACtB,eAAK,SAAS,GAAG,CAAC;AAAA,QACpB;AACA;AAAA,MACF;AAAA,IACF;AAGA,SAAK,gBAAgB,GAAG;AAAA,EAC1B;AAAA,EAEA,gBAAgB,KAAK;AACnB,QAAI,IAAI,KAAK,MAAM;AACnB,QAAI,IAAI,KAAK,MAAM;AACnB,QAAI,UAAU;AACd,QAAI,UAAU;AACd,QAAI,IAAI;AAGR,UAAM,SAAS,IAAI,MAAM,aAAa;AACtC,QAAI,QAAQ;AACV,UAAI,KAAK,WAAW,OAAO,CAAC,CAAC;AAAA,IAC/B;AAGA,UAAM,SAAS,IAAI,MAAM,aAAa;AACtC,QAAI,QAAQ;AACV,UAAI,KAAK,WAAW,OAAO,CAAC,CAAC;AAAA,IAC/B;AAGA,UAAM,SAAS,IAAI,MAAM,aAAa;AACtC,QAAI,QAAQ;AACV,gBAAU,KAAK,WAAW,OAAO,CAAC,CAAC;AAAA,IACrC;AAGA,UAAM,SAAS,IAAI,MAAM,aAAa;AACtC,QAAI,QAAQ;AACV,gBAAU,KAAK,WAAW,OAAO,CAAC,CAAC;AAAA,IACrC;AAGA,UAAM,SAAS,IAAI,MAAM,QAAQ;AACjC,QAAI,QAAQ;AACV,UAAI,SAAS,OAAO,CAAC,CAAC;AAAA,IACxB;AAGA,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,QAAQ,KAAK,MAAM;AACzB,SAAK,MAAM,IAAI;AACf,SAAK,MAAM,IAAI;AAGf,QAAI,MAAM,MAAM;AACd,UAAI,MAAM,GAAG;AACX,YAAI,KAAK,MAAM,YAAY;AAEzB,cAAI,KAAK,MAAM,kBAAkB,UAAU;AAEzC,gBAAI,CAAC,KAAK,uBAAuB;AAC/B,mBAAK,oBAAoB,OAAO,KAAK;AAAA,YACvC;AACA,iBAAK,sBAAsB,KAAK,EAAE,GAAG,EAAE,CAAC;AAAA,UAC1C,OAAO;AAEL,gBAAI,CAAC,KAAK,uBAAuB;AAC/B,mBAAK,oBAAoB,OAAO,KAAK;AAAA,YACvC;AACA,iBAAK,eAAe,OAAO,OAAO,GAAG,GAAG,SAAS,SAAS,KAAK,MAAM,kBAAkB,IAAI;AAAA,UAC7F;AAAA,QACF,OAAO;AAEL,cAAI,KAAK,MAAM,kBAAkB,UAAU;AACzC,iBAAK,UAAU,OAAO,OAAO,GAAG,CAAC;AAEjC,gBAAI,KAAK,MAAM,YAAY,KAAK,UAAU,KAAK,MAAM,QAAQ,GAAG,SAAS,KAAK;AAC1E,kBAAI,QAAQ,KAAK,UAAU,KAAK,MAAM,QAAQ,EAAE,OAAO,CAAC;AACxD,kBAAI,KAAK,MAAM,SAAS,OAAQ,UAAS;AACzC,kBAAI,QAAQ,KAAK,aAAc,SAAQ,KAAK;AAC5C,mBAAK,UAAU,OAAO,OAAO,QAAQ,CAAC;AACtC,mBAAK,UAAU,GAAG,GAAG,QAAQ,CAAC;AAAA,YAClC;AAAA,UACF,OAAO;AAEL,iBAAK,aAAa,OAAO,OAAO,GAAG,GAAG,SAAS,SAAS,KAAK,MAAM,kBAAkB,IAAI;AAAA,UAC3F;AAAA,QACF;AAAA,MACF,WAAW,MAAM,GAAG;AAClB,YAAI,KAAK,MAAM,YAAY;AAExB,eAAK,oBAAoB,GAAG,CAAC;AAAA,QAChC;AAAA,MAEF,WAAW,MAAM,GAAG;AAClB,aAAK,SAAS,GAAG,CAAC;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,oBAAoB,GAAG,GAAG;AAExB,UAAM,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC;AACzB,SAAK,sBAAsB,KAAK,OAAO;AACvC,SAAK,wBAAwB;AAAA,EAC/B;AAAA;AAAA,EAGA,aAAa,IAAI,IAAI,IAAI,IAAI,SAAS,SAAS,MAAM;AACjD,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,UAAM,SAAS,KAAK,KAAK,UAAU,UAAU,UAAU,OAAO;AAM9D,QAAI,SAAS,MAAQ;AACjB,cAAQ,KAAK,gCAAgC;AAC7C;AAAA,IACJ;AAIA,QAAI,aAAa,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE;AAC5C,QAAI,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE;AAG1C,UAAM,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC;AAClE,UAAM,eAAe,OAAO;AAE5B,QAAI,MAAM;AACN,UAAI,cAAc;AACd,mBAAW,aAAa,IAAI,KAAK;AAAA,MACrC,WAAW,YAAY,YAAY;AAC/B,oBAAY,IAAI,KAAK;AAAA,MACzB;AAAA,IACJ,OAAO;AACH,UAAI,cAAc;AACd,mBAAW,aAAa,IAAI,KAAK;AAAA,MACrC,WAAW,YAAY,YAAY;AAC/B,oBAAY,IAAI,KAAK;AAAA,MACzB;AAAA,IACJ;AAIA,UAAM,YAAY,KAAK,IAAI,WAAW,UAAU,IAAI;AAEpD,UAAM,WAAW,KAAK,KAAK,YAAY,GAAG;AAC1C,UAAM,kBAAkB,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,EAAE;AAI1D,QAAI,SAAS;AACb,QAAI,SAAS;AAGb,QAAI,KAAK,MAAM,YAAY,KAAK,UAAU,KAAK,MAAM,QAAQ,GAAG,SAAS,KAAK;AAC1E,UAAI,QAAQ,KAAK,UAAU,KAAK,MAAM,QAAQ,EAAE,OAAO,CAAC;AACxD,UAAI,KAAK,MAAM,SAAS,OAAQ,UAAS;AACzC,UAAI,QAAQ,KAAK,aAAc,SAAQ,KAAK;AAC5C,WAAK,UAAU,IAAI,IAAI,QAAQ,CAAC;AAAA,IACpC;AAEA,aAAS,IAAI,GAAG,KAAK,iBAAiB,KAAK;AACvC,YAAM,IAAI,IAAI;AACd,YAAM,QAAQ,cAAc,WAAW,cAAc;AACrD,YAAM,KAAK,KAAK,SAAS,KAAK,IAAI,KAAK;AACvC,YAAM,KAAK,KAAK,SAAS,KAAK,IAAI,KAAK;AAEvC,WAAK,UAAU,QAAQ,QAAQ,IAAI,EAAE;AAIrC,eAAS;AACT,eAAS;AAAA,IACb;AAGA,QAAI,KAAK,MAAM,YAAY,KAAK,UAAU,KAAK,MAAM,QAAQ,GAAG,SAAS,KAAK;AAC1E,UAAI,QAAQ,KAAK,UAAU,KAAK,MAAM,QAAQ,EAAE,OAAO,CAAC;AACxD,UAAI,KAAK,MAAM,SAAS,OAAQ,UAAS;AACzC,UAAI,QAAQ,KAAK,aAAc,SAAQ,KAAK;AAC5C,WAAK,UAAU,IAAI,IAAI,QAAQ,CAAC;AAAA,IACpC;AAAA,EACJ;AAAA,EAEA,eAAe,IAAI,IAAI,IAAI,IAAI,SAAS,SAAS,MAAM;AACnD,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,UAAM,SAAS,KAAK,KAAK,UAAU,UAAU,UAAU,OAAO;AAE9D,QAAI,SAAS,KAAQ;AAErB,QAAI,aAAa,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE;AAC5C,QAAI,WAAW,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE;AAE1C,UAAM,OAAO,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC;AAClE,UAAM,eAAe,OAAO;AAE5B,QAAI,MAAM;AACN,UAAI,cAAc;AACd,mBAAW,aAAa,IAAI,KAAK;AAAA,MACrC,WAAW,YAAY,YAAY;AAC/B,oBAAY,IAAI,KAAK;AAAA,MACzB;AAAA,IACJ,OAAO;AACH,UAAI,cAAc;AACd,mBAAW,aAAa,IAAI,KAAK;AAAA,MACrC,WAAW,YAAY,YAAY;AAC/B,oBAAY,IAAI,KAAK;AAAA,MACzB;AAAA,IACJ;AAEA,UAAM,YAAY,KAAK,IAAI,WAAW,UAAU,IAAI;AACpD,UAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,YAAY,GAAG,CAAC;AAEvD,aAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAChC,YAAM,IAAI,IAAI;AACd,YAAM,QAAQ,cAAc,WAAW,cAAc;AACrD,UAAI,KAAK,uBAAuB;AAC9B,aAAK,sBAAsB,KAAK;AAAA,UAC9B,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,UAC/B,GAAG,KAAK,SAAS,KAAK,IAAI,KAAK;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,WAAW,KAAK;AAOd,QAAI,IAAI,SAAS,GAAG,EAAG,QAAO,WAAW,GAAG;AAG5C,UAAM,WAAW,KAAK,MAAM,OAAO,UAAU,KAAK,MAAM,OAAO;AAC/D,UAAM,OAAO,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,GAAG,IAAI,IAAI,CAAC,IAAI;AACnE,UAAM,SAAS,OAAO,IAAI,UAAU,CAAC,IAAI;AAEzC,QAAI,MAAM;AASV,UAAMA,SAAQ,KAAK,IAAI,IAAI,KAAK,MAAM,OAAO,OAAO;AACpD,UAAM,SAAS,GAAG,IAAIA;AAGtB,QAAI,KAAK,MAAM,SAAS,OAAQ,QAAO;AAEvC,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,IAAI,IAAI,IAAI,IAAI;AACxB,QAAI,CAAC,KAAK,MAAM,UAAU;AACtB,cAAQ,KAAK,kCAAkC;AAC/C;AAAA,IACJ;AACA,UAAM,KAAK,KAAK,UAAU,KAAK,MAAM,QAAQ;AAC7C,QAAI,CAAC,IAAI;AACL,cAAQ,KAAK,yBAAyB,KAAK,MAAM,QAAQ,cAAc;AACvE;AAAA,IACJ;AAGA,QAAI,QAAQ;AACZ,QAAI,GAAG,SAAS,IAAK,SAAQ,GAAG,OAAO,CAAC;AACxC,QAAI,GAAG,SAAS,IAAK,SAAQ,GAAG,OAAO,CAAC;AAExC,QAAI,KAAK,MAAM,SAAS,OAAQ,UAAS;AAGzC,QAAI,UAAU,GAAG;AACf,cAAQ,KAAK,4BAA4B,KAAK,MAAM,QAAQ,sEAAoB;AAChF,cAAQ;AAAA,IACV;AAGA,QAAI,QAAQ,KAAK,cAAc;AAC7B,cAAQ,KAAK;AAAA,IACf;AAUA,UAAM,KAAK,KAAK;AAChB,UAAM,KAAK,KAAK;AAChB,UAAM,MAAM,KAAK,KAAK,KAAG,KAAK,KAAG,EAAE;AACnC,QAAI,QAAQ,EAAG;AAEf,UAAM,KAAK,CAAC,KAAK,OAAO,QAAQ;AAChC,UAAM,KAAK,KAAK,OAAO,QAAQ;AAG/B,UAAM,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG;AACpC,UAAM,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG;AACpC,UAAM,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG;AACpC,UAAM,KAAK,EAAE,GAAG,KAAK,IAAI,GAAG,KAAK,GAAG;AAEpC,SAAK,QAAQ,IAAI,IAAI,IAAI,EAAE;AAK3B,QAAI,GAAG,SAAS,KAAK;AACjB,WAAK,UAAU,IAAI,IAAI,QAAQ,CAAC;AAChC,WAAK,UAAU,IAAI,IAAI,QAAQ,CAAC;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,SAAS,GAAG,GAAG;AACb,QAAI,CAAC,KAAK,MAAM,SAAU;AAC1B,UAAM,KAAK,KAAK,UAAU,KAAK,MAAM,QAAQ;AAC7C,QAAI,CAAC,GAAI;AAET,QAAI,GAAG,SAAS,KAAK;AAEnB,UAAI,IAAI,GAAG,OAAO,CAAC,IAAI;AACvB,UAAI,KAAK,MAAM,SAAS,OAAQ,MAAK;AAGrC,UAAI,MAAM,GAAG;AACX,gBAAQ,KAAK,4BAA4B,KAAK,MAAM,QAAQ,2FAA+B;AAC3F,YAAI;AAAA,MACN;AAEA,WAAK,UAAU,GAAG,GAAG,CAAC;AAGtB,WAAK,SAAS,KAAK;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QAAG;AAAA,QAAG;AAAA,MACR,CAAC;AAAA,IACH,WAAW,GAAG,SAAS,KAAK;AAC1B,UAAI,IAAI,GAAG,OAAO,CAAC;AACnB,UAAI,IAAI,GAAG,OAAO,CAAC;AACnB,UAAI,KAAK,MAAM,SAAS,QAAQ;AAAE,aAAK;AAAM,aAAK;AAAA,MAAK;AAEvD,YAAM,KAAK,EAAE,GAAG,IAAI,IAAE,GAAG,GAAG,IAAI,IAAE,EAAE;AACpC,YAAM,KAAK,EAAE,GAAG,IAAI,IAAE,GAAG,GAAG,IAAI,IAAE,EAAE;AACpC,YAAM,KAAK,EAAE,GAAG,IAAI,IAAE,GAAG,GAAG,IAAI,IAAE,EAAE;AACpC,YAAM,KAAK,EAAE,GAAG,IAAI,IAAE,GAAG,GAAG,IAAI,IAAE,EAAE;AACpC,WAAK,QAAQ,IAAI,IAAI,IAAI,EAAE;AAG3B,WAAK,SAAS,KAAK;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QAAG;AAAA,QAAG;AAAA,QAAG;AAAA,MACX,CAAC;AAAA,IACH,WAAW,GAAG,SAAS,KAAK;AAC1B,UAAI,IAAI,GAAG,OAAO,CAAC;AACnB,UAAI,IAAI,GAAG,OAAO,CAAC;AACnB,UAAI,KAAK,MAAM,SAAS,QAAQ;AAAE,aAAK;AAAM,aAAK;AAAA,MAAK;AAGvD,WAAK,WAAW,GAAG,GAAG,GAAG,CAAC;AAG1B,WAAK,SAAS,KAAK;AAAA,QACjB,MAAM;AAAA,QACN;AAAA,QAAG;AAAA,QAAG;AAAA,QAAG;AAAA,MACX,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,eAAe;AACb,UAAM,cAAc,KAAK,yBAAyB,CAAC;AACnD,QAAI,YAAY,WAAW,EAAG;AAG9B,UAAM,eAAe,CAAC,QAAQ;AAC5B,UAAI,CAAC,OAAO,IAAI,WAAW,EAAG,QAAO,CAAC;AACtC,YAAM,MAAM,CAAC;AACb,UAAI,OAAO;AACX,iBAAW,KAAK,KAAK;AACnB,YAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,IAAI,KAAK,CAAC,IAAI,QAAQ,KAAK,IAAI,EAAE,IAAI,KAAK,CAAC,IAAI,MAAM;AAC3E,cAAI,KAAK,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,CAAC;AAC3B,iBAAO;AAAA,QACT;AAAA,MACF;AACA,UAAI,IAAI,UAAU,GAAG;AACnB,cAAM,QAAQ,IAAI,CAAC;AACnB,cAAM,MAAM,IAAI,IAAI,SAAS,CAAC;AAC9B,YAAI,KAAK,IAAI,MAAM,IAAI,IAAI,CAAC,IAAI,QAAQ,KAAK,IAAI,MAAM,IAAI,IAAI,CAAC,IAAI,MAAM;AACxE,cAAI,IAAI;AAAA,QACV;AAAA,MACF;AAEA,UAAI,IAAI,SAAS,EAAG,QAAO,CAAC;AAC5B,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,YAAY,IAAI,YAAY,EAAE,OAAO,OAAK,EAAE,UAAU,CAAC;AACxE,QAAI,SAAS,WAAW,EAAG;AAE3B,UAAM,aAAa,CAAC,QAAQ;AAC1B,UAAI,IAAI;AACR,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,cAAM,IAAI,IAAI,CAAC;AACf,cAAM,IAAI,KAAK,IAAI,KAAK,IAAI,MAAM;AAClC,aAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;AAAA,MAC5B;AACA,aAAO,IAAI;AAAA,IACb;AAEA,UAAM,SAAS,CAAC,QAAQ;AACtB,UAAI,OAAO,UAAU,OAAO,UAAU,OAAO,WAAW,OAAO;AAC/D,iBAAW,KAAK,KAAK;AACnB,eAAO,KAAK,IAAI,MAAM,EAAE,CAAC;AACzB,eAAO,KAAK,IAAI,MAAM,EAAE,CAAC;AACzB,eAAO,KAAK,IAAI,MAAM,EAAE,CAAC;AACzB,eAAO,KAAK,IAAI,MAAM,EAAE,CAAC;AAAA,MAC3B;AACA,aAAO,EAAE,MAAM,MAAM,MAAM,KAAK;AAAA,IAClC;AAEA,UAAM,cAAc,CAAC,IAAI,SAAS;AAEhC,UAAI,SAAS;AACb,eAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,IAAI,KAAK,QAAQ,IAAI,KAAK;AAC7D,cAAM,KAAK,KAAK,CAAC,EAAE,GAAG,KAAK,KAAK,CAAC,EAAE;AACnC,cAAM,KAAK,KAAK,CAAC,EAAE,GAAG,KAAK,KAAK,CAAC,EAAE;AACnC,cAAM,YAAc,KAAK,GAAG,MAAQ,KAAK,GAAG,KACzC,GAAG,KAAK,KAAK,OAAO,GAAG,IAAI,OAAQ,KAAK,MAAO,SAAS;AAC3D,YAAI,UAAW,UAAS,CAAC;AAAA,MAC3B;AACA,aAAO;AAAA,IACT;AAGA,UAAM,QAAQ,SAAS,IAAI,CAAC,KAAK,SAAS;AAAA,MACxC,IAAI;AAAA,MACJ;AAAA,MACA,SAAS,KAAK,IAAI,WAAW,GAAG,CAAC;AAAA,MACjC,MAAM,OAAO,GAAG;AAAA,MAChB,QAAQ;AAAA,MACR,OAAO;AAAA,IACT,EAAE;AAEF,eAAW,KAAK,OAAO;AACrB,YAAM,IAAI,EAAE,IAAI,CAAC;AACjB,UAAI,aAAa;AACjB,iBAAW,KAAK,OAAO;AACrB,YAAI,EAAE,OAAO,EAAE,GAAI;AAEnB,YAAI,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE,IAAI,EAAE,KAAK,KAAM;AACtF,YAAI,CAAC,YAAY,GAAG,EAAE,GAAG,EAAG;AAC5B,YAAI,CAAC,cAAc,EAAE,UAAU,WAAW,SAAS;AACjD,uBAAa;AAAA,QACf;AAAA,MACF;AACA,UAAI,WAAY,GAAE,SAAS,WAAW;AAAA,IACxC;AAGA,UAAM,UAAU,CAAC,OAAO;AACtB,UAAI,IAAI;AACR,UAAI,MAAM,MAAM,EAAE;AAClB,aAAO,IAAI,WAAW,IAAI;AACxB;AACA,cAAM,MAAM,IAAI,MAAM;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AACA,eAAW,KAAK,MAAO,GAAE,QAAQ,QAAQ,EAAE,EAAE;AAG7C,UAAM,cAAc,oBAAI,IAAI;AAC5B,eAAW,KAAK,OAAO;AACrB,UAAI,CAAC,YAAY,IAAI,EAAE,MAAM,EAAG,aAAY,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC5D,kBAAY,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;AAAA,IACrC;AAEA,UAAM,iBAAiB,CAAC,UAAU,gBAAgB;AAChD,YAAM,OAAO,CAAC;AACd,YAAM,cAAc,CAAC;AAGrB,iBAAW,KAAK,SAAU,MAAK,KAAK,EAAE,GAAG,EAAE,CAAC;AAC5C,UAAI,SAAS,SAAS;AAGtB,iBAAW,WAAW,aAAa;AACjC,oBAAY,KAAK,MAAM;AACvB,mBAAW,KAAK,QAAS,MAAK,KAAK,EAAE,GAAG,EAAE,CAAC;AAC3C,kBAAU,QAAQ;AAAA,MACpB;AAEA,YAAM,YAAY,OAAO,MAAM,YAAY,SAAS,cAAc,IAAI;AACtE,UAAI,CAAC,aAAa,UAAU,WAAW,EAAG;AAE1C,YAAM,YAAY,KAAK,SAAS,SAAS;AACzC,eAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,aAAK,SAAS,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC;AAC1C,aAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,MAC1B;AACA,eAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,aAAK,gBAAgB,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,MACpD;AAAA,IACF;AAEA,eAAW,KAAK,OAAO;AACrB,UAAI,EAAE,QAAQ,MAAM,EAAG;AACvB,YAAM,WAAW,YAAY,IAAI,EAAE,EAAE,KAAK,CAAC;AAC3C,YAAM,cAAc,SACjB,IAAI,QAAM,MAAM,EAAE,CAAC,EACnB,OAAO,OAAK,EAAE,UAAU,EAAE,QAAQ,CAAC,EACnC,IAAI,OAAK,EAAE,GAAG;AAEjB,qBAAe,EAAE,KAAK,WAAW;AAAA,IACnC;AAAA,EACF;AAAA,EAEA,QAAQ,IAAI,IAAI,IAAI,IAAI;AACtB,UAAM,YAAY,KAAK,SAAS,SAAS;AAEzC,SAAK,SAAS,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;AAAG,SAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAC3D,SAAK,SAAS,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;AAAG,SAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAC3D,SAAK,SAAS,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;AAAG,SAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAC3D,SAAK,SAAS,KAAK,GAAG,GAAG,GAAG,GAAG,CAAC;AAAG,SAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAK3D,SAAK,gBAAgB,KAAK,WAAW,YAAU,GAAG,YAAU,CAAC;AAC7D,SAAK,gBAAgB,KAAK,WAAW,YAAU,GAAG,YAAU,CAAC;AAAA,EAC/D;AAAA,EAEA,UAAU,IAAI,IAAI,GAAG;AAEnB,QAAI,MAAM,GAAG;AACX,UAAI;AAAA,IACN;AAGA,UAAM,gBAAgB,IAAI,KAAK,KAAK;AACpC,UAAM,WAAW,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,gBAAgB,GAAG,GAAG,EAAE,GAAG,EAAE;AAC1E,UAAM,YAAY,KAAK,SAAS,SAAS;AAEzC,SAAK,SAAS,KAAK,IAAI,IAAI,CAAC;AAC5B,SAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAExB,aAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,YAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,WAAK,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,CAAC;AACxE,WAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,IAC1B;AAEA,aAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,WAAK,gBAAgB,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,WAAW,IAAI,IAAI,GAAG,GAAG;AAEvB,QAAI,MAAM,EAAG,KAAI;AACjB,QAAI,MAAM,EAAG,KAAI;AAGjB,QAAI,IAAI,GAAG;AAET,YAAM,SAAS,IAAI;AACnB,YAAM,YAAY,IAAI;AAEtB,YAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAGlD,YAAM,SAAS,KAAK,YAAY;AAGhC,UAAI,YAAY,KAAK,SAAS,SAAS;AACvC,WAAK,SAAS,KAAK,QAAQ,IAAI,CAAC;AAChC,WAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAExB,eAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAElC,cAAM,QAAQ,KAAK,KAAG,IAAK,IAAI,WAAY,KAAK;AAChD,aAAK,SAAS,KAAK,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AACtF,aAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,MAC1B;AAGA,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,aAAK,gBAAgB,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,MAC/E;AAGA,UAAI,YAAY,GAAG;AACjB,cAAM,KAAK,EAAE,GAAG,KAAK,YAAU,GAAG,GAAG,KAAK,OAAO;AACjD,cAAM,KAAK,EAAE,GAAG,KAAK,YAAU,GAAG,GAAG,KAAK,OAAO;AACjD,cAAM,KAAK,EAAE,GAAG,KAAK,YAAU,GAAG,GAAG,KAAK,OAAO;AACjD,cAAM,KAAK,EAAE,GAAG,KAAK,YAAU,GAAG,GAAG,KAAK,OAAO;AAGjD,aAAK,QAAQ,IAAI,IAAI,IAAI,EAAE;AAAA,MAC7B;AAGA,YAAM,UAAU,KAAK,YAAY;AAGjC,kBAAY,KAAK,SAAS,SAAS;AACnC,WAAK,SAAS,KAAK,SAAS,IAAI,CAAC;AACjC,WAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAExB,eAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAElC,cAAM,QAAQ,CAAC,KAAK,KAAG,IAAK,IAAI,WAAY,KAAK;AACjD,aAAK,SAAS,KAAK,UAAU,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AACvF,aAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,MAC1B;AAGA,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,aAAK,gBAAgB,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,MAC/E;AAAA,IAEF,OAAO;AAEL,YAAM,SAAS,IAAI;AACnB,YAAM,aAAa,IAAI;AACvB,YAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAGlD,YAAM,QAAQ,KAAK,aAAa;AAGhC,UAAI,YAAY,KAAK,SAAS,SAAS;AACvC,WAAK,SAAS,KAAK,IAAI,OAAO,CAAC;AAC/B,WAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAExB,eAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAElC,cAAM,QAAS,IAAI,WAAY,KAAK;AACpC,aAAK,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,QAAQ,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AACrF,aAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,MAC1B;AAGA,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,aAAK,gBAAgB,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,MAC/E;AAGA,UAAI,aAAa,GAAG;AAClB,cAAM,KAAK,EAAE,GAAG,KAAK,QAAQ,GAAG,KAAK,aAAW,EAAE;AAClD,cAAM,KAAK,EAAE,GAAG,KAAK,QAAQ,GAAG,KAAK,aAAW,EAAE;AAClD,cAAM,KAAK,EAAE,GAAG,KAAK,QAAQ,GAAG,KAAK,aAAW,EAAE;AAClD,cAAM,KAAK,EAAE,GAAG,KAAK,QAAQ,GAAG,KAAK,aAAW,EAAE;AAClD,aAAK,QAAQ,IAAI,IAAI,IAAI,EAAE;AAAA,MAC7B;AAGA,YAAM,WAAW,KAAK,aAAa;AAGnC,kBAAY,KAAK,SAAS,SAAS;AACnC,WAAK,SAAS,KAAK,IAAI,UAAU,CAAC;AAClC,WAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAExB,eAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAElC,cAAM,QAAQ,KAAK,KAAM,IAAI,WAAY,KAAK;AAC9C,aAAK,SAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,WAAW,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AACxF,aAAK,OAAO,KAAK,GAAG,GAAG,CAAC;AAAA,MAC1B;AAGA,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,aAAK,gBAAgB,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,MAC/E;AAAA,IACF;AAAA,EACF;AACF;;;AC13BA,OAAO,WAAW;AAClB,SAAS,OAAO,MAAM,iBAAiB;AACvC,OAAOC,aAAY;AAInB,IAAM,SAAS,SAAS,eAAe,QAAQ;AAC/C,IAAM,YAAY,SAAS,eAAe,WAAW;AACrD,IAAM,SAAS,SAAS,eAAe,QAAQ;AAC/C,IAAM,WAAW,SAAS,eAAe,UAAU;AACnD,IAAM,WAAW,SAAS,eAAe,UAAU;AACnD,IAAM,WAAW,SAAS,eAAe,UAAU;AACnD,IAAM,YAAY,SAAS,eAAe,WAAW;AACrD,IAAM,iBAAiB,SAAS,eAAe,gBAAgB;AAC/D,IAAM,yBAAyB,SAAS,eAAe,wBAAwB;AAC/E,IAAM,iBAAiB,SAAS,eAAe,gBAAgB;AAG/D,SAAS,cAAc;AACrB,MAAI,eAAgB,gBAAe,UAAU,IAAI,QAAQ;AAC3D;AAGA,SAAS,cAAc;AACrB,MAAI,eAAgB,gBAAe,UAAU,OAAO,QAAQ;AAC9D;AAGA,IAAI,KAAK,OAAO,WAAW,SAAS;AAAA,EAClC,SAAS;AAAA;AAAA,EACT,OAAO;AAAA,EACP,uBAAuB;AACzB,CAAC;AACD,IAAI,UAAU;AACd,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI,mBAAmB;AACvB,IAAI,kBAAkB;AACtB,IAAM,kBAAkB;AAAA,EACtB;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EACT;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EACT;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AAAA,EACT;AAAA,EAAG;AAAA,EAAG;AAAA,EAAG;AACX;AAIA,IAAI,SAAS,CAAC;AACd,IAAI,QAAQ;AAAZ,IAAe,SAAS;AAAxB,IAA2B,SAAS;AACpC,IAAI,aAAa;AACjB,IAAI,aAAa;AAAjB,IAAoB,aAAa;AACjC,IAAI,kBAAkB;AAAtB,IAAyB,kBAAkB;AAM3C,IAAI,mBAAmB;AAGvB,IAAM,aAAa;AAAA,EACjB;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAC5C;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAC5C;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAC5C;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAC9C;AAMA,SAAS,UAAU,KAAK;AACtB,SAAO,MAAM,WAAW,SAAS,WAAW,GAAG,IAAI;AACrD;AAGA,IAAI,gBAAgB,oBAAI,IAAI;AAC5B,IAAI,aAAa;AACjB,IAAI,iBAAiB,CAAC;AACtB,IAAI,uBAAuB;AAE3B,IAAI,CAAC,IAAI;AACP,QAAM,qBAAqB;AAC7B,OAAO;AACL,YAAU;AACV,UAAQ;AACV;AAEA,SAAS,YAAY;AACjB,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOnB,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASjB,QAAM,MAAM,GAAG,aAAa,wBAAwB;AACpD,MAAI,CAAC,KAAK;AACR,YAAQ,KAAK,sCAAsC;AAAA,EACrD;AAEA,QAAM,KAAK,aAAa,IAAI,GAAG,eAAe,QAAQ;AACtD,QAAM,KAAK,aAAa,IAAI,GAAG,iBAAiB,QAAQ;AACxD,YAAU,cAAc,IAAI,IAAI,EAAE;AAElC,qBAAmB,GAAG,kBAAkB,SAAS,YAAY;AAC7D,mBAAiB,GAAG,mBAAmB,SAAS,SAAS;AACzD,mBAAiB,GAAG,mBAAmB,SAAS,SAAS;AACzD,oBAAkB,GAAG,mBAAmB,SAAS,UAAU;AAG3D,qBAAmB,GAAG,aAAa;AACnC,KAAG,WAAW,GAAG,cAAc,gBAAgB;AAC/C,KAAG,WAAW,GAAG,cAAc,IAAI,aAAa;AAAA,IAC9C;AAAA,IAAI;AAAA,IAAI;AAAA,IACP;AAAA,IAAG;AAAA,IAAI;AAAA,IACP;AAAA,IAAI;AAAA,IAAG;AAAA,IACR;AAAA,IAAK;AAAA,IAAG;AAAA,EACV,CAAC,GAAG,GAAG,WAAW;AAElB,oBAAkB,GAAG,aAAa;AAClC,KAAG,WAAW,GAAG,sBAAsB,eAAe;AACtD,KAAG,WAAW,GAAG,sBAAsB,IAAI,YAAY,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,WAAW;AAO1F,KAAG,OAAO,GAAG,KAAK;AAElB,KAAG,UAAU,GAAG,WAAW,GAAG,GAAG;AAEjC,SAAO;AAEP,SAAO,iBAAiB,UAAU,MAAM;AAExC,SAAO,MAAM,SAAS;AAEtB,SAAO,iBAAiB,aAAa,OAAK;AACxC,QAAI,EAAE,WAAW,EAAG;AACpB,iBAAa;AACb,iBAAa,EAAE;AACf,iBAAa,EAAE;AACf,sBAAkB;AAClB,sBAAkB;AAClB,WAAO,MAAM,SAAS;AAAA,EACxB,CAAC;AAED,SAAO,iBAAiB,aAAa,OAAK;AACxC,QAAI,CAAC,WAAY;AAEjB,UAAM,SAAS,EAAE,UAAU;AAC3B,UAAM,SAAS,EAAE,UAAU;AAC3B,UAAM,OAAO,OAAO,sBAAsB;AAG1C,UAAM,mBAAoB,SAAS,KAAK,QAAS;AACjD,UAAM,mBAAmB,EAAE,SAAS,KAAK,UAAU;AAEnD,aAAS,kBAAkB;AAC3B,aAAS,kBAAkB;AAAA,EAC7B,CAAC;AAED,SAAO,iBAAiB,WAAW,MAAM;AACvC,QAAI,YAAY;AACd,mBAAa;AACb,aAAO,MAAM,SAAS;AAAA,IACxB;AAAA,EACF,CAAC;AAED,SAAO,iBAAiB,cAAc,MAAM;AAC1C,QAAI,YAAY;AACd,mBAAa;AACb,aAAO,MAAM,SAAS;AAAA,IACxB;AAAA,EACF,CAAC;AACD,SAAO,iBAAiB,SAAS,OAAK;AACpC,MAAE,eAAe;AAGjB,UAAM,OAAO,OAAO,sBAAsB;AAC1C,UAAM,SAAS,EAAE,UAAU,KAAK;AAChC,UAAM,SAAS,EAAE,UAAU,KAAK;AAGhC,UAAM,cAAe,SAAS,KAAK,QAAS,IAAI;AAChD,UAAM,cAAc,EAAG,SAAS,KAAK,SAAU,IAAI;AAEnD,UAAM,eAAe,KAAK,QAAQ,KAAK;AAIvC,UAAM,gBAAgB,cAAc,WAAW,QAAQ;AACvD,UAAM,gBAAgB,cAAc,UAAU;AAG9C,UAAM,cAAc,EAAE,SAAS,IAAI,MAAM;AACzC,aAAS;AAKT,aAAS,cAAc,gBAAgB,QAAQ;AAC/C,aAAS,cAAc,eAAe;AAAA,EACxC,CAAC;AACH;AAEA,SAAS,aAAaC,KAAI,MAAM,QAAQ;AACtC,QAAM,SAASA,IAAG,aAAa,IAAI;AACnC,EAAAA,IAAG,aAAa,QAAQ,MAAM;AAC9B,EAAAA,IAAG,cAAc,MAAM;AACvB,MAAI,CAACA,IAAG,mBAAmB,QAAQA,IAAG,cAAc,GAAG;AACrD,YAAQ,MAAMA,IAAG,iBAAiB,MAAM,CAAC;AACzC,IAAAA,IAAG,aAAa,MAAM;AACtB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,cAAcA,KAAI,IAAI,IAAI;AACjC,QAAM,IAAIA,IAAG,cAAc;AAC3B,EAAAA,IAAG,aAAa,GAAG,EAAE;AACrB,EAAAA,IAAG,aAAa,GAAG,EAAE;AACrB,EAAAA,IAAG,YAAY,CAAC;AAChB,SAAO;AACT;AAEA,SAAS,SAAS;AAChB,QAAM,WAAW,OAAO;AACxB,QAAM,YAAY,OAAO;AAEzB,SAAO,QAAQ,OAAO;AACtB,SAAO,SAAS,OAAO;AACvB,KAAG,SAAS,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;AAG7C,MAAI,WAAW,KAAK,YAAY,KAAK,OAAO,SAAS,GAAG;AACtD,UAAM,YAAY,WAAW;AAC7B,UAAM,YAAY,OAAO,QAAQ,OAAO;AAmBxC,aAAS,UAAU,YAAY;AAAA,EACjC;AAGA,MAAI,OAAO,SAAS,GAAG;AACrB,uBAAmB;AACnB,0BAAsB,MAAM;AAC1B,UAAI,OAAO,WAAW,YAAY;AAChC,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAOA,SAAS,SAAS,KAAK;AACrB,QAAM,SAAS,4CAA4C,KAAK,GAAG;AACnE,SAAO,SAAS;AAAA,IACd,GAAG,SAAS,OAAO,CAAC,GAAG,EAAE,IAAI;AAAA,IAC7B,GAAG,SAAS,OAAO,CAAC,GAAG,EAAE,IAAI;AAAA,IAC7B,GAAG,SAAS,OAAO,CAAC,GAAG,EAAE,IAAI;AAAA,EAC/B,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AACzB;AAOA,IAAI,4BAA4B;AAEhC,SAAS,wCAAwC,MAAMC,WAAU;AAC/D,MAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AAEvC,MAAI,WAAW,KAAK,IAAI,EAAG;AAE3B,QAAM,UAAU,KAAK,MAAM,sCAAsC;AACjE,MAAI,CAAC,QAAS;AAEd,QAAM,SAAS,QAAQ,CAAC,KAAK,OAAO,IAAI,YAAY,MAAM,MAAM,MAAM;AACtE,QAAM,gBAAgB,SAAS,QAAQ,CAAC,GAAG,EAAE;AAC7C,QAAM,gBAAgB,SAAS,QAAQ,CAAC,GAAG,EAAE;AAC7C,MAAI,CAAC,OAAO,SAAS,aAAa,KAAK,CAAC,OAAO,SAAS,aAAa,EAAG;AAExE,MAAI,OAAO;AACX,QAAM,UAAU,KAAK,MAAM,gBAAgB;AAC3C,MAAI,SAAS;AACX,WAAO,QAAQ,CAAC,EAAE,YAAY,MAAM,OAAO,OAAO;AAAA,EACpD,OAAO;AACL,QAAI,YAAY,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI,EAAG,QAAO;AAC3D,QAAI,YAAY,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI,EAAG,QAAO;AAAA,EAC7D;AAEA,8BAA4B;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,aAAa,gBAAgB;AAAA,IAC7B,QAAQ,CAAC,eAAe,aAAa;AAAA,IACrC;AAAA,IACA;AAAA,IACA,MAAMA;AAAA,EACR;AACA,UAAQ;AAAA,IACN,qCAAYA,SAAQ,sCAAkB,SAAS,MAAM,OAAO,IAAI,IAAI,aAAa,IAAI,aAAa,UAAU,QAAQ,SAAS;AAAA,EAC/H;AACF;AAEA,eAAe,mBAAmB,MAAMA,WAAU;AAChD,MAAI;AAyqBF,QAASC,gBAAT,SAAsB,GAAGC,QAAO;AAC9B,YAAM,SAAS,CAAC;AAChB,YAAM,WAAW,EAAE,MAAM,8BAA8B,KAAK,CAAC;AAC7D,UAAI,WAAW,GAAG,WAAW;AAC7B,UAAI,SAAS,GAAG,SAAS;AAEzB,eAAS,QAAQ,SAAO;AACtB,cAAM,OAAO,IAAI,CAAC,EAAE,YAAY;AAChC,cAAM,aAAa,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,YAAY;AACjD,cAAM,OAAO,IAAI,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,QAAQ,EAAE,OAAO,OAAK,CAAC,EAAE,IAAI,UAAU;AAE9E,gBAAQ,MAAM;AAAA,UACZ,KAAK;AACH,gBAAI,KAAK,UAAU,GAAG;AACpB,yBAAW,aAAa,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC;AACnD,yBAAW,aAAa,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC;AACnD,uBAAS;AACT,uBAAS;AACT,qBAAO,KAAK,WAAWA,QAAO,WAAWA,SAAS,EAAG;AAAA,YACvD;AACA;AAAA,UACF,KAAK;AACH,gBAAI,KAAK,UAAU,GAAG;AACpB,yBAAW,aAAa,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC;AACnD,yBAAW,aAAa,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC;AACnD,qBAAO,KAAK,WAAWA,QAAO,WAAWA,SAAS,EAAG;AAAA,YACvD;AACA;AAAA,UACF,KAAK;AACH,gBAAI,KAAK,UAAU,GAAG;AACpB,yBAAW,aAAa,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC;AACnD,qBAAO,KAAK,WAAWA,QAAO,WAAWA,SAAS,EAAG;AAAA,YACvD;AACA;AAAA,UACF,KAAK;AACH,gBAAI,KAAK,UAAU,GAAG;AACpB,yBAAW,aAAa,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC;AACnD,qBAAO,KAAK,WAAWA,QAAO,WAAWA,SAAS,EAAG;AAAA,YACvD;AACA;AAAA,UACF,KAAK;AACH,gBAAI,aAAa,UAAU,aAAa,QAAQ;AAC9C,qBAAO,KAAK,SAASA,QAAO,SAASA,SAAS,EAAG;AAAA,YACnD;AACA;AAAA,QACJ;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAjDS,uBAAAD;AAxqBT,YAAQ,IAAI,yCAAqBD,SAAQ,EAAE;AAG3C,UAAM,cAAc,WAAW,KAAK,IAAI;AACxC,UAAM,WAAW,cAAc,UAAU;AACzC,YAAQ,IAAI,0CAAsB,QAAQ,EAAE;AAG5C,QAAI,iBAAiB;AACrB,QAAI,aAAa;AACjB,QAAI,YAAY;AAChB,QAAI,aAAa;AAEf,YAAM,QAAQ,KAAK,MAAM,IAAI,EAAE,IAAI,UAAQ,KAAK,KAAK,CAAC;AAGtD,iBAAW,QAAQ,OAAO;AACxB,YAAI,CAAC,YAAY;AACf,cAAI,aAAa,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI,EAAG,cAAa;AAClE,cAAI,WAAW,KAAK,IAAI,KAAK,UAAU,KAAK,IAAI,EAAG,cAAa;AAAA,QAClE;AACA,YAAI,CAAC,WAAW;AACd,cAAI,UAAU,KAAK,IAAI,EAAG,aAAY;AACtC,cAAI,UAAU,KAAK,IAAI,EAAG,aAAY;AAAA,QACxC;AACA,YAAI,cAAc,UAAW;AAAA,MAC/B;AAEA,iBAAW,QAAQ,OAAO;AACxB,cAAM,cAAc,KAAK,MAAM,gCAAgC;AAC/D,YAAI,aAAa;AACf,gBAAM,gBAAgB,SAAS,YAAY,CAAC,CAAC;AAC7C,gBAAM,gBAAgB,SAAS,YAAY,CAAC,CAAC;AAC7C,2BAAiB;AAAA,YACf;AAAA,YACA;AAAA,YACA,aAAa,gBAAgB;AAAA,YAC7B,QAAQ,CAAC,eAAe,aAAa;AAAA,UACvC;AACA,kBAAQ,IAAI,gDAAuB,aAAa,IAAI,aAAa,KAAK,aAAa,uBAAQ,aAAa,qBAAM;AAC9G;AAAA,QACF;AAAA,MACF;AAGA,UAAI,YAAY;AAChB,YAAM,kBAAkB,KAAK,MAAM,cAAc;AACjD,UAAI,iBAAiB;AACnB,mBAAW,SAAS,iBAAiB;AACnC,gBAAM,SAAS,MAAM,UAAU,CAAC,EAAE,QAAQ,MAAM,EAAE,EAAE;AACpD,sBAAY,KAAK,IAAI,WAAW,MAAM;AAAA,QACxC;AAAA,MACF;AAGA,UAAI,CAAC,gBAAgB;AACnB,YAAI,6BAA6B,0BAA0B,QAAQ;AACjE,2BAAiB;AAAA,YACf,eAAe,0BAA0B;AAAA,YACzC,eAAe,0BAA0B;AAAA,YACzC,aAAa,0BAA0B,gBAAgB,0BAA0B;AAAA,YACjF,QAAQ,CAAC,0BAA0B,eAAe,0BAA0B,aAAa;AAAA,UAC3F;AACA,kBAAQ;AAAA,YACN,4EAA8C,0BAA0B,IAAI,mBAAS,eAAe,aAAa,IAAI,eAAe,aAAa;AAAA,UACnJ;AAAA,QACF,WAAW,YAAY,GAAG;AAExB,gBAAM,gBAAgB,KAAK,IAAI,GAAG,SAAS;AAC3C,gBAAM,gBAAgB,KAAK,IAAI,GAAG,YAAY,aAAa;AAC3D,2BAAiB;AAAA,YACf;AAAA,YACA;AAAA,YACA,aAAa,gBAAgB;AAAA,YAC7B,QAAQ,CAAC,eAAe,aAAa;AAAA,UACvC;AACA,kBAAQ;AAAA,YACN,gHAA+C,aAAa,IAAI,aAAa,eAAe,SAAS;AAAA,UACvG;AAAA,QACF;AAAA,MACF;AAGA,UAAI,CAAC,cAAc,6BAA6B,0BAA0B,MAAM;AAC9E,qBAAa,0BAA0B,SAAS,OAAO,OAAO;AAAA,MAChE;AACA,UAAI,CAAC,aAAa,6BAA6B,0BAA0B,MAAM;AAC7E,oBAAY,0BAA0B,SAAS,MAAM,OAAO;AAAA,MAC9D;AAGA,UAAI,gBAAgB;AAClB,cAAM,eAAe;AACrB,YAAI,cAAc;AAGhB,gBAAM,gBAAgB,eAAe;AACrC,kBAAQ,IAAI,2CAAuB,eAAe,aAAa,KAAK,eAAe,aAAa,MAAM,aAAa,SAAI;AACvH,kBAAQ,IAAI,sDAAwB,SAAS,QAAG;AAGhD,cAAI,YAAY,eAAe;AAC7B,kBAAM,cAAc,YAAY;AAChC,2BAAe,iBAAiB;AAChC,2BAAe,cAAc,eAAe,gBAAgB,eAAe;AAC3E,2BAAe,SAAS,CAAC,eAAe,eAAe,eAAe,aAAa;AACnF,oBAAQ,IAAI,oEAA4B,eAAe,aAAa,KAAK,eAAe,aAAa,MAAM,SAAS,SAAI;AAGxH,kBAAM,mBAAmB;AACzB,kBAAM,eAAe,eAAe,eAAe,aAAa,IAAI,eAAe,aAAa;AAEhG,gBAAI,iBAAiB,KAAK,IAAI,GAAG;AAC/B,qBAAO,KAAK,QAAQ,kBAAkB,YAAY;AAClD,sBAAQ,IAAI,qFAA8B,YAAY,EAAE;AAAA,YAC1D;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,QAAI,cAAc;AAClB,QAAI,eAAe,kBAAkB,eAAe,QAAQ;AAE1D,YAAM,YAAY,aAAa,MAAM,YAAY,MAAM,OAAO,MAAM;AACpE,YAAM,YAAY;AAAA;AAAA,QAEhB,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ,EAAE,QAAQ,eAAe,QAAQ,MAAM,SAAS;AAAA,MAC1D;AACA,UAAI;AACF,gBAAQ;AAAA,UACN,yEAA2C,eAAe,OAAO,CAAC,CAAC,IAAI,eAAe,OAAO,CAAC,CAAC,WAAW,QAAQ,WAAW,cAAc,MAAM;AAAA,QACnJ;AACA,sBAAc,MAAM,MAAM,SAAS;AAAA,MACrC,SAAS,GAAG;AACV,gBAAQ,KAAK,2GAA0C,GAAG,WAAW,CAAC,EAAE;AACxE,sBAAc;AAAA,MAChB;AAAA,IACF;AACA,QAAI,CAAC,aAAa;AAChB,oBAAc,MAAM,MAAM,EAAE,UAAU,SAAS,CAAC;AAAA,IAClD;AACA,UAAM,aAAa,KAAK,WAAW;AAGnC,YAAQ,IAAI,kDAAwC,WAAW,UAAU,MAAM;AAG/E,UAAM,UAAU,UAAU,UAAU;AACpC,YAAQ,IAAI,0EAAuC,SAAS,UAAU,MAAM;AAG5E,UAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,UAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,UAAM,gBAAgB,aAAa,YAAY;AAG/C,UAAM,iBACJ,kBAAkB,UAClB,kBAAkB,SAClB,aAAa,KAAK,aAAa,KAC/B,cAAc,SAAS,SAAS,KAChC,iBAAiB,KAAK,aAAa,KACnC,cAAc,SAAS,IAAI,KAC3B,cAAc,SAAS,OAAO,KAC9B,cAAc,WAAW,KAAK,KAC9B,cAAc,WAAW,KAAK;AAGhC,UAAM,WAAW,iBAAiB,MAAM;AAGxC,QAAI,gBAAgB;AAClB,cAAQ,IAAI,wBAAS,YAAY,cAAc,QAAQ,IAAI;AAAA,IAC7D;AAEA,UAAM,WAAW,CAAC;AAClB,UAAM,UAAU,CAAC;AAIjB,UAAM,oBAAoB,cAAc,CAAC,IAAI;AAG7C,UAAM,eAAe,CAAC;AACtB,QAAI,kBAAkB;AACtB,QAAI,kBAAkB;AACtB,UAAM,iBAAiB,CAAC,iBAAiB;AACvC,YAAM,IAAK,gBAAgB;AAC3B,UAAI,MAAM,gBAAiB;AAC3B,UAAI,QAAQ,SAAS,iBAAiB;AACpC,qBAAa,KAAK,EAAE,UAAU,iBAAiB,OAAO,iBAAiB,KAAK,QAAQ,OAAO,CAAC;AAAA,MAC9F;AACA,wBAAkB;AAClB,wBAAkB,QAAQ;AAAA,IAC5B;AACA,UAAME,SAAQ,WAAW,UAAU,OAAO,OAAO;AAOjD,QAAI,mBAAmB;AACvB,UAAM,kBAAkB,CAAC,WAAW,KAAK,aAAa;AACpD,UAAI,CAAC,OAAO,aAAa,KAAM,QAAO;AACtC,YAAM,IAAI,OAAO,SAAS,EAAE,KAAK;AACjC,YAAM,OAAO,EAAE,WAAW,GAAG,IAAI,KAAK;AACtC,UAAI,SAAS,EAAE,QAAQ,WAAW,EAAE;AACpC,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,OAAO,IAAI;AACjB,UAAI,QAAQ,OAAO,SAAS,MAAM;AAGhC,aAAK,YAAY,IAAI,YAAY,MAAM,MAAM;AAC3C,mBAAS,OAAO,SAAS,MAAM,GAAG;AAAA,QACpC,OAAO;AACL,mBAAS,OAAO,OAAO,MAAM,GAAG;AAAA,QAClC;AAAA,MACF;AACA,YAAM,QAAQ,KAAK,IAAI,IAAI,IAAI,iBAAiB,CAAC;AACjD,YAAM,IAAI,SAAS,QAAQ,EAAE;AAC7B,UAAI,CAAC,OAAO,SAAS,CAAC,KAAK,UAAU,EAAG,QAAO;AAC/C,aAAO,QAAQ,IAAI;AAAA,IACrB;AAEA,QAAI,eAAe,kBAAkB,MAAM,QAAQ,WAAW,QAAQ,KAAK,WAAW,SAAS,SAAS,GAAG;AACzG,UAAI;AACF,cAAM,UAAU,KAAK,MAAM,sBAAsB;AACjD,cAAM,cAAc,WAAW,SAAS;AAAA,UAAK,OAC3C,GAAG,SAAS,gBACZ,EAAE,OAAO,SAAS,YAClB,OAAO,SAAS,EAAE,MAAM,EAAE,KAC1B,OAAO,SAAS,EAAE,MAAM,EAAE;AAAA,QAC5B;AAEA,YAAI,WAAW,aAAa;AAC1B,gBAAM,OAAO,YAAY,MAAM;AAC/B,gBAAM,OAAO,YAAY,MAAM;AAG/B,gBAAM,YAAY,WAAW,UAAU,OAAO,OAAO;AACrD,gBAAM,WAAW,cAAc;AAE/B,gBAAM,eAAe,CAAC,aAAa;AACjC,gBAAI,OAAO,gBAAgB,QAAQ,CAAC,GAAG,gBAAgB,QAAQ;AAC/D,gBAAI,OAAO,gBAAgB,QAAQ,CAAC,GAAG,gBAAgB,QAAQ;AAC/D,gBAAI,QAAQ,QAAQ,QAAQ,MAAM;AAChC,kBAAI,aAAa,QAAQ,cAAc,MAAM;AAAE,wBAAQ;AAAM,wBAAQ;AAAA,cAAK;AAC1E,kBAAI,aAAa,QAAQ,cAAc,MAAM;AAAE,wBAAQ;AAAM,wBAAQ;AAAA,cAAK;AAAA,YAC5E;AACA,kBAAM,SAAS,CAAC;AAChB,gBAAI,OAAO,SAAS,IAAI,KAAK,SAAS,EAAG,QAAO,KAAK,OAAO,IAAI;AAChE,gBAAI,OAAO,SAAS,IAAI,KAAK,SAAS,EAAG,QAAO,KAAK,OAAO,IAAI;AAChE,gBAAI,OAAO,WAAW,EAAG,QAAO;AAChC,kBAAM,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO;AACvD,kBAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,kBAAM,MAAM,KAAK,IAAI,GAAG,MAAM;AAC9B,kBAAM,SAAU,QAAQ,IAAK,KAAK,IAAI,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;AACnE,mBAAO,EAAE,UAAU,MAAM,MAAM,KAAK,OAAO;AAAA,UAC7C;AAIA,gBAAM,eAAe,aAAa,IAAI,YAAY,MAAM,OAAO,OAAO;AACtE,gBAAM,UAAU,gBAAgB,OAAO,OAAO;AAC9C,gBAAM,UAAU,aAAa,WAAW;AACxC,gBAAM,MAAM,aAAa,OAAO;AAEhC,gBAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,SAAS,EAAE,GAAG,KAAK,EAAE,SAAS,QAAQ,KAAK,IAAI,EAAE,MAAM,CAAC,KAAK;AAE9F,cAAI,MAAM,OAAO,GAAG;AAAA,UAEpB,WAAW,MAAM,GAAG,GAAG;AACrB,oBAAQ,KAAK,uEAA+B,WAAW,8CAAW,OAAO,6FAAiC;AAAA,UAC5G,OAAO;AAEL,kBAAM,SAAS,WAAW;AAC1B,gBAAI,UAAU,OAAO,SAAS,OAAO,GAAG,KAAK,KAAK,IAAI,OAAO,MAAM,CAAC,IAAI,QAAQ,OAAO,SAAS,MAAM;AACpG,oBAAM,SAAS,KAAK,IAAI,OAAO,GAAG;AAClC,oBAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,MAAM,MAAM,CAAC,CAAC;AACzD,oBAAM,UAAW,KAAK,IAAI,SAAS,KAAK,IAAI,QAAS,OAAO,QAAQ;AACpE,kBAAI,WAAW,QAAQ,WAAW,KAAO;AACvC,mCAAmB;AACnB,wBAAQ,KAAK,yLAAsE,gBAAgB,WAAW,SAAS,UAAU,OAAO,QAAQ,GAAG;AAAA,cACrJ;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,SAAS,GAAG;AACV,gBAAQ,KAAK,sGAA+C,GAAG,WAAW,CAAC;AAAA,MAC7E;AAAA,IACF;AAEA,UAAM,WAAWA,SAAQ;AACzB,UAAM,YAAYA;AAKlB,UAAM,oBAAoB,CAAC,YAAY;AACrC,UAAI,CAAC,WAAW,CAAC,QAAQ,QAAS;AAElC,YAAM,UAAU,QAAQ,QAAQ,YAAY;AAG5C,UAAI,YAAY,UAAU;AACxB,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAC/E,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA,SAAS;AACxF,cAAM,IAAI,WAAW,QAAQ,YAAY,KAAK,QAAQ,YAAY,KAAK,CAAC,IAAIA;AAE5E,YAAI,IAAI,GAAG;AACT,gBAAM,WAAW,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,GAAG,EAAE,GAAG,EAAE;AAC5E,gBAAM,YAAY,SAAS,SAAS;AAGpC,mBAAS,KAAK,IAAI,IAAI,CAAC;AAGvB,mBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,kBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,qBAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,KAAK,IAAI,GAAG,CAAC;AAAA,UACrE;AAGA,mBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,oBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,UAClE;AAGA,mBAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,qBAAS,KAAK,IAAI,IAAI,CAAC;AAAA,UACzB;AAAA,QACF;AAAA,MACF,WAES,YAAY,WAAW;AAC9B,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAC/E,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA,SAAS;AACxF,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAC/E,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAE/E,YAAI,KAAK,KAAK,KAAK,GAAG;AACpB,gBAAM,WAAW,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,IAAI,EAAE,IAAI,GAAG,GAAG,EAAE,GAAG,EAAE;AAC3F,gBAAM,YAAY,SAAS,SAAS;AAGpC,mBAAS,KAAK,IAAI,IAAI,CAAC;AAGvB,mBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,kBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,qBAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC;AAAA,UACvE;AAGA,mBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,oBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,UAClE;AAGA,mBAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,qBAAS,KAAK,IAAI,IAAI,CAAC;AAAA,UACzB;AAAA,QACF;AAAA,MACF,WAES,YAAY,QAAQ;AAC3B,cAAM,IAAI,WAAW,QAAQ,YAAY,KAAK,QAAQ,YAAY,KAAK,CAAC,IAAIA;AAC5E,cAAM,IAAI,WAAW,QAAQ,YAAY,KAAK,QAAQ,YAAY,KAAK,CAAC,IAAIA,SAAS;AACrF,cAAM,QAAQ,WAAW,QAAQ,YAAY,SAAS,QAAQ,YAAY,SAAS,CAAC,IAAIA;AACxF,cAAM,SAAS,WAAW,QAAQ,YAAY,UAAU,QAAQ,YAAY,UAAU,CAAC,IAAIA;AAC3F,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAC/E,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAE/E,YAAI,QAAQ,KAAK,SAAS,GAAG;AAE3B,cAAI,KAAK,KAAK,KAAK,GAAG;AAEpB,kBAAM,eAAe,KAAK,IAAI,IAAI,EAAE;AAGpC,gBAAI,QAAQ,QAAQ;AAElB,oBAAM,SAAS,SAAS;AACxB,oBAAM,YAAY,QAAQ;AAG1B,oBAAM,SAAS,IAAI;AACnB,oBAAM,SAAS,IAAI;AACnB,oBAAM,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC;AACnD,kBAAI,YAAY,SAAS,SAAS;AAClC,uBAAS,KAAK,QAAQ,QAAQ,CAAC;AAC/B,uBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,sBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,yBAAS,KAAK,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,cACvF;AACA,uBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,wBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,cAClE;AAGA,kBAAI,YAAY,GAAG;AACjB,sBAAM,YAAY,SAAS,SAAS;AACpC,yBAAS,KAAK,IAAI,QAAQ,GAAG,CAAC;AAC9B,yBAAS,KAAK,IAAI,SAAS,WAAW,GAAG,CAAC;AAC1C,yBAAS,KAAK,IAAI,SAAS,WAAW,IAAI,QAAQ,CAAC;AACnD,yBAAS,KAAK,IAAI,QAAQ,IAAI,QAAQ,CAAC;AACvC,wBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AACpD,wBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AAAA,cACtD;AAGA,oBAAM,UAAU,IAAI,QAAQ;AAC5B,oBAAM,UAAU,IAAI;AACpB,0BAAY,SAAS,SAAS;AAC9B,uBAAS,KAAK,SAAS,SAAS,CAAC;AACjC,uBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,sBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,yBAAS,KAAK,UAAU,KAAK,IAAI,KAAK,IAAI,QAAQ,UAAU,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,cACzF;AACA,uBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,wBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,cAClE;AAAA,YACF,OAAO;AAEL,oBAAM,SAAS,QAAQ;AACvB,oBAAM,aAAa,SAAS;AAG5B,oBAAM,QAAQ,IAAI;AAClB,oBAAM,QAAQ,IAAI;AAClB,oBAAM,WAAW,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC;AACnD,kBAAI,YAAY,SAAS,SAAS;AAClC,uBAAS,KAAK,OAAO,OAAO,CAAC;AAC7B,uBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,sBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,yBAAS,KAAK,QAAQ,KAAK,IAAI,KAAK,IAAI,QAAQ,QAAQ,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,cACrF;AACA,uBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,wBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,cAClE;AAGA,kBAAI,aAAa,GAAG;AAClB,sBAAM,YAAY,SAAS,SAAS;AACpC,yBAAS,KAAK,GAAG,IAAI,QAAQ,CAAC;AAC9B,yBAAS,KAAK,IAAI,OAAO,IAAI,QAAQ,CAAC;AACtC,yBAAS,KAAK,IAAI,OAAO,IAAI,SAAS,YAAY,CAAC;AACnD,yBAAS,KAAK,GAAG,IAAI,SAAS,YAAY,CAAC;AAC3C,wBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AACpD,wBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AAAA,cACtD;AAGA,oBAAM,WAAW,IAAI;AACrB,oBAAM,WAAW,IAAI,SAAS;AAC9B,0BAAY,SAAS,SAAS;AAC9B,uBAAS,KAAK,UAAU,UAAU,CAAC;AACnC,uBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,sBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,yBAAS,KAAK,WAAW,KAAK,IAAI,KAAK,IAAI,QAAQ,WAAW,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,cAC3F;AACA,uBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,wBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,cAClE;AAAA,YACF;AAAA,UACF,OAAO;AAEL,kBAAM,YAAY,SAAS,SAAS;AAEpC,qBAAS,KAAK,GAAG,GAAG,CAAC;AACrB,qBAAS,KAAK,IAAI,OAAO,GAAG,CAAC;AAC7B,qBAAS,KAAK,IAAI,OAAO,IAAI,QAAQ,CAAC;AACtC,qBAAS,KAAK,GAAG,IAAI,QAAQ,CAAC;AAE9B,oBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AACpD,oBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AAAA,UACtD;AAAA,QACF;AAAA,MACF,WAES,YAAY,QAAQ;AAC3B,cAAM,IAAI,QAAQ,YAAY,KAAK,QAAQ,YAAY,KAAK;AAC5D,cAAM,OAAO,QAAQ,YAAY,QAAQ,QAAQ,YAAY,QAAQ;AACrE,cAAM,SAAS,QAAQ,YAAY,UAAU,QAAQ,YAAY,UAAU;AAG3E,cAAM,aAAa,QAAQ,SAAS;AACpC,cAAM,eAAe,UAAU,WAAW;AAE1C,YAAI,GAAG;AACL,gBAAM,aAAaD,cAAa,GAAGC,MAAK;AAGxC,cAAI,cAAc,cAAc,WAAW,UAAU,GAAG;AACtD,gBAAI;AACF,oBAAM,YAAYC,QAAO,UAAU;AACnC,kBAAI,aAAa,UAAU,SAAS,GAAG;AACrC,sBAAM,YAAY,SAAS,SAAS;AACpC,yBAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,2BAAS,KAAK,WAAW,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,CAAC;AAAA,gBACnD;AACA,yBAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,0BAAQ,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,gBACvC;AAAA,cACF;AAAA,YACF,SAAS,GAAG;AACV,sBAAQ,KAAK,mDAA0B,CAAC;AAAA,YAC1C;AAAA,UACF,WAES,gBAAgB,cAAc,WAAW,UAAU,GAAG;AAC7D,gBAAI,cAAc,WAAW,QAAQ,aAAa,cAAc,KAAK,QAAQ,aAAa,cAAc,KAAK,IAAI,IAAID;AACrH,gBAAI,cAAc,SAAU,eAAc;AAE1C,kBAAM,SAAS,cAAc;AAG7B,qBAAS,IAAI,GAAG,IAAI,WAAW,SAAS,GAAG,KAAK,GAAG;AACjD,oBAAM,KAAK,WAAW,CAAC;AACvB,oBAAM,KAAK,WAAW,IAAI,CAAC;AAC3B,oBAAM,KAAK,WAAW,IAAI,CAAC;AAC3B,oBAAM,KAAK,WAAW,IAAI,CAAC;AAE3B,oBAAM,KAAK,KAAK;AAChB,oBAAM,KAAK,KAAK;AAChB,oBAAM,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AAEvC,kBAAI,MAAM,MAAO;AACf,sBAAM,KAAK,CAAC,KAAK,MAAM;AACvB,sBAAM,KAAK,KAAK,MAAM;AAGtB,sBAAM,YAAY,SAAS,SAAS;AACpC,yBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,yBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,yBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,yBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AAEjC,wBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AACpD,wBAAQ,KAAK,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAGxD,oBAAI,MAAM,GAAG;AACX,wBAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAClD,wBAAM,YAAY,SAAS,SAAS;AACpC,2BAAS,KAAK,IAAI,IAAI,CAAC;AAEvB,2BAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,0BAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,6BAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,kBAC/E;AAEA,2BAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,4BAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,kBAClE;AAAA,gBACF;AAGA;AACE,wBAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAClD,wBAAM,YAAY,SAAS,SAAS;AACpC,2BAAS,KAAK,IAAI,IAAI,CAAC;AAEvB,2BAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,0BAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,6BAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,kBAC/E;AAEA,2BAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,4BAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,kBAClE;AAAA,gBACF;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAES,YAAY,QAAQ;AAC3B,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAC/E,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA,SAAS;AACxF,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA;AAC/E,cAAM,KAAK,WAAW,QAAQ,YAAY,MAAM,QAAQ,YAAY,MAAM,CAAC,IAAIA,SAAS;AAGxF,YAAI,cAAc,WAAW,QAAQ,aAAa,cAAc,KAAK,QAAQ,aAAa,cAAc,KAAK,IAAI,IAAIA;AAGrH,YAAI,cAAc,SAAU,eAAc;AAE1C,cAAM,SAAS,cAAc;AAG7B,cAAM,KAAK,KAAK;AAChB,cAAM,KAAK,KAAK;AAChB,cAAM,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AACvC,YAAI,MAAM,GAAG;AACX,gBAAM,KAAK,CAAC,KAAK,MAAM;AACvB,gBAAM,KAAK,KAAK,MAAM;AAGtB,gBAAM,YAAY,SAAS,SAAS;AACpC,mBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,mBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,mBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,mBAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AAEjC,kBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AACpD,kBAAQ,KAAK,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAGxD,gBAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAClD,cAAI,YAAY,SAAS,SAAS;AAClC,mBAAS,KAAK,IAAI,IAAI,CAAC;AACvB,mBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,kBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,qBAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,UAC/E;AACA,mBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,oBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,UAClE;AAGA,sBAAY,SAAS,SAAS;AAC9B,mBAAS,KAAK,IAAI,IAAI,CAAC;AACvB,mBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,kBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,qBAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,UAC/E;AACA,mBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,oBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,UAClE;AAAA,QACF;AAAA,MACF,WAES,YAAY,cAAc,YAAY,WAAW;AACxD,cAAM,YAAY,QAAQ,YAAY,UAAU,QAAQ,YAAY,UAAU;AAC9E,YAAI,WAAW;AACb,gBAAM,SAAS,UAAU,KAAK,EAAE,MAAM,QAAQ,EAAE,IAAI,UAAU;AAC9D,cAAI,OAAO,UAAU,GAAG;AACtB,kBAAM,aAAa,CAAC;AACpB,qBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,GAAG;AACzC,kBAAI,CAAC,MAAM,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC,GAAG;AAC9C,2BAAW,KAAK,OAAO,CAAC,IAAIA,QAAO,OAAO,IAAI,CAAC,IAAIA,SAAS,EAAG;AAAA,cACjE;AAAA,YACF;AAEA,gBAAI,WAAW,UAAU,GAAG;AAC1B,kBAAI;AACF,sBAAM,YAAYC,QAAO,UAAU;AACnC,oBAAI,aAAa,UAAU,SAAS,GAAG;AACrC,wBAAM,YAAY,SAAS,SAAS;AACpC,2BAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,6BAAS,KAAK,WAAW,CAAC,GAAG,WAAW,IAAI,CAAC,GAAG,CAAC;AAAA,kBACnD;AACA,2BAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,4BAAQ,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,kBACvC;AAAA,gBACF;AAAA,cACF,SAAS,GAAG;AACV,wBAAQ,KAAK,kDAAyB,CAAC;AAAA,cACzC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,QAAQ,YAAY,QAAQ,SAAS,SAAS,GAAG;AACnD,gBAAQ,SAAS,QAAQ,WAAS,kBAAkB,KAAK,CAAC;AAAA,MAC5D;AAAA,IACF;AA2DA,QAAI,CAAC,eAAe,WAAW,QAAQ,UAAU;AAC/C,cAAQ,SAAS,QAAQ,WAAS,kBAAkB,KAAK,CAAC;AAC1D,cAAQ,IAAI,6CAAyB,SAAS,SAAS,CAAC,kBAAQ,QAAQ,SAAS,CAAC,qBAAM;AAAA,IAC1F;AAGA,QAAI,WAAW,YAAY,WAAW,SAAS,SAAS,GAAG;AACzD,YAAM,iBAAiB,SAAS,SAAS;AACzC,cAAQ,IAAI,8DAA0C,WAAW,SAAS,MAAM,UAAK;AAIrF,YAAM,YAAY,CAAC;AACnB,iBAAW,SAAS,QAAQ,WAAS;AACnC,kBAAU,MAAM,IAAI,KAAK,UAAU,MAAM,IAAI,KAAK,KAAK;AAAA,MACzD,CAAC;AAMD,iBAAW,SAAS,QAAQ,CAAC,OAAO,QAAQ;AAE1C,uBAAe,MAAM,YAAY,MAAM;AAKvC,YAAI,MAAM,SAAS,eAAe,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAG7E,gBAAM,SAAS,QAAQ,CAAC,SAAS,WAAW;AAC1C,gBAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,QAAQ,KAAK;AAC3D,oBAAM,SAAS,QAAQ,MAAM,CAAC,IAAID;AAClC,oBAAM,SAAS,QAAQ,MAAM,CAAC,IAAIA;AAClC,oBAAM,OAAO,QAAQ,IAAI,CAAC,IAAIA;AAC9B,oBAAM,OAAO,QAAQ,IAAI,CAAC,IAAIA;AAE9B,oBAAM,YAAY,SAAS,SAAS;AACpC,uBAAS,KAAK,QAAQ,QAAQ,CAAC;AAC/B,uBAAS,KAAK,MAAM,MAAM,CAAC;AAG3B,kBAAI,SAAU,QAAQ,SAAS,MAAM,SAAS,QAAQA;AAGtD,kBAAI,QAAQ,SAAU,SAAQ;AAE9B,oBAAM,SAAS,QAAQ;AAEvB,oBAAM,KAAK,OAAO;AAClB,oBAAM,KAAK,OAAO;AAClB,oBAAM,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AACvC,kBAAI,MAAM,GAAG;AACX,sBAAM,KAAK,CAAC,KAAK,MAAM;AACvB,sBAAM,KAAK,KAAK,MAAM;AAGtB,sBAAM,KAAK,SAAS,SAAS;AAC7B,yBAAS,KAAK,SAAS,IAAI,SAAS,IAAI,CAAC;AACzC,yBAAS,KAAK,SAAS,IAAI,SAAS,IAAI,CAAC;AACzC,yBAAS,KAAK,OAAO,IAAI,OAAO,IAAI,CAAC;AACrC,yBAAS,KAAK,OAAO,IAAI,OAAO,IAAI,CAAC;AAErC,wBAAQ,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC;AAC/B,wBAAQ,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAGnC,oBAAI,WAAW,GAAG;AAChB,wBAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAClD,wBAAM,YAAY,SAAS,SAAS;AACpC,2BAAS,KAAK,QAAQ,QAAQ,CAAC;AAE/B,2BAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,0BAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,6BAAS,KAAK,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,SAAS,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,kBACvF;AAEA,2BAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,4BAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,kBAClE;AAAA,gBACF;AAGA;AACE,wBAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC;AAClD,wBAAM,YAAY,SAAS,SAAS;AACpC,2BAAS,KAAK,MAAM,MAAM,CAAC;AAE3B,2BAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,0BAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,6BAAS,KAAK,OAAO,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,kBACnF;AAEA,2BAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,4BAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,kBAClE;AAAA,gBACF;AAAA,cACF;AAAA,YACF,WAAW,QAAQ,SAAS,OAAO;AAEjC,oBAAM,KAAK,QAAQ,MAAM,CAAC;AAC1B,oBAAM,KAAK,QAAQ,MAAM,CAAC;AAC1B,oBAAM,KAAK,QAAQ,IAAI,CAAC;AACxB,oBAAM,KAAK,QAAQ,IAAI,CAAC;AACxB,oBAAM,MAAM,QAAQ,OAAO,CAAC;AAC5B,oBAAM,MAAM,QAAQ,OAAO,CAAC;AAE5B,oBAAM,SAAS,KAAKA;AACpB,oBAAM,SAAS,KAAKA;AACpB,oBAAM,OAAO,KAAKA;AAClB,oBAAM,OAAO,KAAKA;AAClB,oBAAM,KAAK,MAAMA;AACjB,oBAAM,KAAK,MAAMA;AAEjB,oBAAM,aAAc,QAAQ,MAAM,SAAS,KAAK,OAAO,QAAQ,MAAM,CAAC,MAAM,WACxE,QAAQ,MAAM,CAAC,IACf,KAAK,MAAM,KAAK,KAAK,KAAK,GAAG;AACjC,oBAAM,WAAY,QAAQ,IAAI,SAAS,KAAK,OAAO,QAAQ,IAAI,CAAC,MAAM,WAClE,QAAQ,IAAI,CAAC,IACb,KAAK,MAAM,KAAK,KAAK,KAAK,GAAG;AAEjC,oBAAM,aAAc,OAAO,QAAQ,WAAW,YAAY,QAAQ,SAAS,IACvE,QAAQ,SACR,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,KAAKA;AAGhE,oBAAM,eAAe,KAAK,IAAI,SAAS,IAAI,IAAI,QAAS,KAAK,IAAI,SAAS,IAAI,IAAI;AAClF,kBAAI,YAAY,WAAW;AAE3B,kBAAI,cAAc;AAEhB,4BAAY,KAAK,KAAK;AACtB,oBAAI,MAAM,GAAG;AAAA,gBAEb;AAAA,cACF,WAAW,KAAK,IAAI,SAAS,IAAI,MAAO;AAEtC;AAAA,cACF;AAEA,oBAAM,cAAc,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,IAAI,SAAS,IAAI,YAAY,GAAG,CAAC;AAEhF,kBAAI,YAAa,QAAQ,SAAS,MAAM,SAAS,QAAQA;AAGzD,kBAAI,WAAW,SAAU,YAAW;AAEpC,oBAAM,aAAa,WAAW;AAE9B,uBAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,sBAAM,KAAK,IAAI;AACf,sBAAM,MAAM,IAAI,KAAK;AACrB,sBAAM,SAAS,aAAa,YAAY;AACxC,sBAAM,SAAS,aAAa,YAAY;AAExC,sBAAM,KAAK,KAAK,KAAK,IAAI,MAAM,IAAI;AACnC,sBAAM,KAAK,KAAK,KAAK,IAAI,MAAM,IAAI;AACnC,sBAAM,KAAK,KAAK,KAAK,IAAI,MAAM,IAAI;AACnC,sBAAM,KAAK,KAAK,KAAK,IAAI,MAAM,IAAI;AAGnC,sBAAM,KAAK,KAAK;AAChB,sBAAM,KAAK,KAAK;AAChB,sBAAM,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AACvC,oBAAI,MAAM,GAAG;AACX,wBAAM,KAAK,CAAC,KAAK,MAAM;AACvB,wBAAM,KAAK,KAAK,MAAM;AAEtB,wBAAM,KAAK,SAAS,SAAS;AAC7B,2BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,2BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,2BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,2BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AAEjC,0BAAQ,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC;AAC/B,0BAAQ,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAAA,gBACrC;AAAA,cACF;AAGA,kBAAI,CAAC,gBAAgB,WAAW,GAAG;AACjC,sBAAM,cAAc,KAAK,IAAI,GAAG,KAAK,KAAK,aAAa,CAAC,CAAC;AACzD,sBAAM,YAAY,SAAS,SAAS;AACpC,yBAAS,KAAK,QAAQ,QAAQ,CAAC;AAE/B,yBAAS,IAAI,GAAG,KAAK,aAAa,KAAK;AACrC,wBAAM,QAAS,IAAI,cAAe,KAAK,KAAK;AAC5C,2BAAS,KAAK,SAAS,KAAK,IAAI,KAAK,IAAI,YAAY,SAAS,KAAK,IAAI,KAAK,IAAI,YAAY,CAAC;AAAA,gBAC/F;AAEA,yBAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,0BAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,gBAClE;AAAA,cACF;AAGA,kBAAI,CAAC,cAAc;AACjB,sBAAM,cAAc,KAAK,IAAI,GAAG,KAAK,KAAK,aAAa,CAAC,CAAC;AACzD,sBAAM,YAAY,SAAS,SAAS;AACpC,yBAAS,KAAK,MAAM,MAAM,CAAC;AAE3B,yBAAS,IAAI,GAAG,KAAK,aAAa,KAAK;AACrC,wBAAM,QAAS,IAAI,cAAe,KAAK,KAAK;AAC5C,2BAAS,KAAK,OAAO,KAAK,IAAI,KAAK,IAAI,YAAY,OAAO,KAAK,IAAI,KAAK,IAAI,YAAY,CAAC;AAAA,gBAC3F;AAEA,yBAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,0BAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,gBAClE;AAAA,cACF;AAAA,YACF;AAAA,UACF,CAAC;AAAA,QACH,WAES,MAAM,SAAS,gBAAgB,MAAM,OAAO;AACnD,gBAAM,QAAQ,MAAM;AACpB,gBAAM,WAAW,MAAM,YAAY;AAUnC,gBAAM,aAAa,CAAC,IAAI,IAAI,MAAM;AAChC,gBAAI,EAAE,IAAI,GAAI;AAEd,kBAAM,IAAI,KAAK;AACf,kBAAM,IAAI,KAAK;AACf,kBAAM,SAAS,IAAI;AAEnB,kBAAM,WAAW,KAAK,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,SAAS,GAAG,GAAG,EAAE,GAAG,EAAE;AACjF,kBAAM,YAAY,SAAS,SAAS;AAGpC,qBAAS,KAAK,GAAG,GAAG,CAAC;AAErB,qBAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,oBAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,uBAAS,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC;AAAA,YAC7E;AACA,qBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,sBAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,YAClE;AAGA,qBAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,uBAAS,KAAK,GAAG,GAAG,CAAC;AAAA,YACvB;AAGA,gBAAI,mBAAmB;AACrB,oBAAM,WAAW,SAAS,SAAS;AACnC,gCAAkB,KAAK,EAAE,OAAO,WAAW,OAAO,WAAW,WAAW,IAAI,GAAG,IAAI,GAAG,MAAM,SAAS,CAAC;AAAA,YACxG;AAAA,UACF;AAEA,gBAAM,gBAAgB,CAAC,GAAG,GAAG,OAAO,OAAO,MAAM;AAC/C,kBAAM,MAAM,KAAK,KAAK;AACtB,kBAAM,MAAM,KAAK,KAAK;AACtB,kBAAM,KAAK,SAAS,KAAK;AACzB,kBAAM,KAAK,SAAS,KAAK;AACzB,gBAAI,EAAE,IAAI,KAAK,IAAI,GAAI;AAGvB,kBAAM,YAAY,SAAS,SAAS;AACpC,qBAAS,KAAK,IAAI,IAAI,CAAC;AACvB,qBAAS,KAAK,KAAK,GAAG,IAAI,CAAC;AAC3B,qBAAS,KAAK,KAAK,GAAG,KAAK,GAAG,CAAC;AAC/B,qBAAS,KAAK,IAAI,KAAK,GAAG,CAAC;AAC3B,oBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AACpD,oBAAQ,KAAK,WAAW,YAAY,GAAG,YAAY,CAAC;AAKpD,kBAAM,KAAK,KAAK,IAAI;AACpB,kBAAM,KAAK,KAAK,IAAI;AACpB,qBAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,uBAAS,KAAK,IAAI,IAAI,CAAC;AAAA,YACzB;AAGA,gBAAI,mBAAmB;AACrB,oBAAM,WAAW,SAAS,SAAS;AACnC,gCAAkB,KAAK,EAAE,OAAO,WAAW,OAAO,WAAW,WAAW,IAAI,IAAI,MAAM,YAAY,CAAC;AAAA,YACrG;AAAA,UACF;AAEA,gBAAM,cAAc,CAAC,QAAQ;AAC3B,gBAAI,CAAC,MAAM,QAAQ,GAAG,KAAK,IAAI,SAAS,EAAG;AAC3C,kBAAM,OAAO,CAAC;AACd,uBAAW,KAAK,KAAK;AACnB,kBAAI,CAAC,KAAK,EAAE,SAAS,EAAG;AACxB,mBAAK,KAAK,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI,QAAQ;AAAA,YAC5C;AACA,gBAAI,KAAK,SAAS,EAAG;AAErB,kBAAM,YAAYC,QAAO,IAAI;AAC7B,gBAAI,CAAC,aAAa,UAAU,WAAW,EAAG;AAC1C,kBAAM,YAAY,SAAS,SAAS;AACpC,qBAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,uBAAS,KAAK,KAAK,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,CAAC;AAAA,YACvC;AACA,qBAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,sBAAQ,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,YACvC;AAAA,UACF;AAEA,gBAAM,cAAc,CAAC,aAAa;AAChC,gBAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,EAAG;AACvD,kBAAM,MAAM,CAAC;AACb,gBAAI,UAAU;AAEd,kBAAM,YAAY,CAAC,GAAG,MAAM;AAC1B,kBAAI,KAAK,IAAI,UAAU,IAAI,QAAQ;AAAA,YACrC;AAEA,uBAAW,OAAO,UAAU;AAC1B,kBAAI,CAAC,OAAO,CAAC,IAAI,KAAM;AAEvB,kBAAI,IAAI,SAAS,UAAU,IAAI,SAAS,IAAI,KAAK;AAC/C,oBAAI,CAAC,SAAS;AACZ,4BAAU,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC;AACpC,4BAAU;AAAA,gBACZ;AACA,0BAAU,IAAI,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;AAAA,cAClC,WAAW,IAAI,SAAS,SAAS,IAAI,SAAS,IAAI,OAAO,IAAI,QAAQ;AAEnE,sBAAM,KAAK,IAAI,OAAO,CAAC,IAAI;AAC3B,sBAAM,KAAK,IAAI,OAAO,CAAC,IAAI;AAC3B,sBAAM,UAAU,IAAI,UAAU,KAAK,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK;AAElI,sBAAM,aAAc,IAAI,MAAM,SAAS,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC;AAC/H,oBAAI,WAAY,IAAI,IAAI,SAAS,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC;AAEnH,oBAAI,WAAW,WAAY,aAAY,KAAK,KAAK;AAEjD,oBAAI,CAAC,SAAS;AACZ,4BAAU,IAAI,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC;AACpC,4BAAU;AAAA,gBACZ;AAEA,sBAAM,SAAS,KAAK,IAAI,WAAW,UAAU,IAAI;AACjD,sBAAM,cAAc,KAAK,IAAI,GAAG,KAAK,KAAK,SAAS,GAAG,CAAC;AACvD,yBAAS,IAAI,GAAG,KAAK,aAAa,KAAK;AACrC,wBAAM,IAAI,IAAI;AACd,wBAAM,QAAQ,cAAc,WAAW,cAAc;AACrD,wBAAM,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI;AACjC,wBAAM,IAAI,KAAK,KAAK,IAAI,KAAK,IAAI;AACjC,sBAAI,KAAK,GAAG,CAAC;AAAA,gBACf;AAAA,cACF;AAAA,YACF;AAEA,gBAAI,IAAI,SAAS,EAAG;AACpB,kBAAM,YAAYA,QAAO,GAAG;AAC5B,gBAAI,CAAC,aAAa,UAAU,WAAW,EAAG;AAC1C,kBAAM,YAAY,SAAS,SAAS;AACpC,qBAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;AACtC,uBAAS,KAAK,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;AAAA,YACrC;AACA,qBAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,sBAAQ,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,YACvC;AAAA,UACF;AAEA,gBAAM,kBAAkB,CAAC,MAAM;AAC7B,gBAAI,CAAC,KAAK,CAAC,EAAE,KAAM;AACnB,gBAAI,EAAE,SAAS,UAAU;AACvB,yBAAW,EAAE,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK,CAAC;AAAA,YAC3C,WAAW,EAAE,SAAS,aAAa;AACjC,4BAAc,EAAE,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS,GAAG,EAAE,SAAS,GAAG,EAAE,KAAK,CAAC;AAAA,YACxE,WAAW,EAAE,SAAS,WAAW;AAC/B,0BAAY,EAAE,UAAU,CAAC,CAAC;AAAA,YAC5B,WAAW,EAAE,SAAS,WAAW;AAC/B,0BAAY,EAAE,YAAY,CAAC,CAAC;AAAA,YAC9B;AAAA,UACF;AAGA,cAAI,MAAM,SAAS,kBAAkB,MAAM,QAAQ,MAAM,MAAM,GAAG;AAChE,uBAAW,OAAO,MAAM,QAAQ;AAC9B,oBAAM,gBAAgB,OAAO,IAAI,UAAU,OAAO,UAAU;AAC5D,6BAAe,aAAa;AAC5B,8BAAgB,GAAG;AAAA,YACrB;AACA,2BAAe,QAAQ;AAAA,UACzB,OAAO;AAEL,4BAAgB,KAAK;AAAA,UACvB;AAAA,QACF,WAES,MAAM,SAAS,iBAAiB,MAAM,YAAY,MAAM,SAAS,SAAS,GAAG;AAQpF,gBAAM,eAAe,CAAC;AACtB,gBAAM,cAAc,CAAC;AACrB,cAAI,UAAU;AAEd,gBAAM,SAAS,QAAQ,CAAC,SAAS,WAAW;AAC1C,gBAAI,QAAQ,QAAQ,MAAM;AAC1B,gBAAI,QAAQ;AACZ,gBAAI,YAAY;AAEhB,gBAAI,QAAQ,SAAS,QAAQ;AAC3B,uBAAS,QAAQ,MAAM,CAAC,IAAID;AAC5B,uBAAS,QAAQ,MAAM,CAAC,IAAIA;AAC5B,qBAAO,QAAQ,IAAI,CAAC,IAAIA;AACxB,qBAAO,QAAQ,IAAI,CAAC,IAAIA;AAAA,YAC1B,WAAW,QAAQ,SAAS,OAAO;AACjC,sBAAQ;AAIR,oBAAM,KAAK,QAAQ,MAAM,CAAC;AAC1B,oBAAM,KAAK,QAAQ,MAAM,CAAC;AAC1B,oBAAM,KAAK,QAAQ,IAAI,CAAC;AACxB,oBAAM,KAAK,QAAQ,IAAI,CAAC;AAExB,uBAAS,KAAKA;AACd,uBAAS,KAAKA;AACd,qBAAO,KAAKA;AACZ,qBAAO,KAAKA;AAEZ,oBAAM,MAAM,QAAQ,OAAO,CAAC;AAC5B,oBAAM,MAAM,QAAQ,OAAO,CAAC;AAC5B,oBAAM,KAAK,MAAMA;AACjB,oBAAM,KAAK,MAAMA;AAEjB,oBAAM,aAAc,QAAQ,MAAM,SAAS,KAAK,OAAO,QAAQ,MAAM,CAAC,MAAM,WACxE,QAAQ,MAAM,CAAC,IACf,KAAK,MAAM,KAAK,KAAK,KAAK,GAAG;AACjC,oBAAM,WAAY,QAAQ,IAAI,SAAS,KAAK,OAAO,QAAQ,IAAI,CAAC,MAAM,WAClE,QAAQ,IAAI,CAAC,IACb,KAAK,MAAM,KAAK,KAAK,KAAK,GAAG;AAEjC,oBAAM,UAAW,OAAO,QAAQ,WAAW,YAAY,QAAQ,SAAS,IACpE,QAAQ,SACR,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC;AAC3D,oBAAM,SAAS,UAAUA;AAGzB,kBAAI,YAAY,WAAW;AAG3B,oBAAM,cAAc,KAAK,IAAI,KAAK,EAAE,IAAI,QAAQ,KAAK,IAAI,KAAK,EAAE,IAAI;AACpE,kBAAI,eAAe,KAAK,IAAI,SAAS,IAAI,MAAM;AAC7C,4BAAY,KAAK,KAAK;AAAA,cACxB;AAEA,0BAAY,EAAE,IAAI,IAAI,QAAQ,YAAY,UAAU;AAAA,YACtD;AAGA,gBAAI,eAAe;AACnB,gBAAI,SAAS;AACX,oBAAM,OAAO,KAAK,KAAK,KAAK,IAAI,SAAS,QAAQ,GAAG,CAAC,IAAI,KAAK,IAAI,SAAS,QAAQ,GAAG,CAAC,CAAC;AACxF,kBAAI,OAAO,MAAM;AACf,+BAAe;AACf,4BAAY,KAAK,aAAa,SAAS,CAAC;AAAA,cAC1C;AAAA,YACF,OAAO;AACL,6BAAe;AAAA,YACjB;AAGA,gBAAI,cAAc;AAChB,2BAAa,KAAK,QAAQ,MAAM;AAAA,YAClC;AAEA,gBAAI,SAAS,WAAW;AAEtB,oBAAM,EAAE,IAAI,IAAI,QAAQ,YAAY,UAAU,IAAI;AAClD,oBAAM,YAAY,KAAK,IAAI,SAAS,IAAI;AACxC,oBAAM,cAAc,KAAK,IAAI,GAAG,KAAK,KAAK,YAAY,GAAG,CAAC;AAE1D,uBAAS,IAAI,GAAG,KAAK,aAAa,KAAK;AACrC,sBAAM,IAAI,IAAI;AACd,sBAAM,QAAQ,aAAa,YAAY;AACvC,6BAAa;AAAA,kBACX,KAAK,KAAK,IAAI,KAAK,IAAI;AAAA,kBACvB,KAAK,KAAK,IAAI,KAAK,IAAI;AAAA,gBACzB;AAAA,cACF;AAAA,YACF,OAAO;AAEL,2BAAa,KAAK,MAAM,IAAI;AAAA,YAC9B;AAEA,sBAAU,EAAE,GAAG,MAAM,GAAG,KAAK;AAAA,UAC/B,CAAC;AAGD,cAAI,aAAa,UAAU,GAAG;AAC5B,gBAAI;AACF,oBAAM,YAAYC,QAAO,cAAc,WAAW;AAElD,kBAAI,UAAU,SAAS,GAAG;AACxB,sBAAM,YAAY,SAAS,SAAS;AAGpC,yBAAS,IAAI,GAAG,IAAI,aAAa,QAAQ,KAAK,GAAG;AAC/C,2BAAS,KAAK,aAAa,CAAC,GAAG,aAAa,IAAI,CAAC,GAAG,CAAC;AAAA,gBACvD;AAGA,yBAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,0BAAQ,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,gBACvC;AAGA,oBAAI,kBAAkB,WAAW,GAAG;AAClC,wBAAM,eAAe,WAAW;AAGhC,2BAAS,IAAI,GAAG,IAAI,aAAa,SAAS,GAAG,KAAK,GAAG;AACnD,0BAAM,KAAK,aAAa,CAAC;AACzB,0BAAM,KAAK,aAAa,IAAI,CAAC;AAC7B,0BAAM,KAAK,aAAa,IAAI,CAAC;AAC7B,0BAAM,KAAK,aAAa,IAAI,CAAC;AAE7B,0BAAM,KAAK,KAAK;AAChB,0BAAM,KAAK,KAAK;AAChB,0BAAM,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AAEvC,wBAAI,MAAM,MAAO;AACf,4BAAM,KAAK,CAAC,KAAK,MAAM;AACvB,4BAAM,KAAK,KAAK,MAAM;AAGtB,4BAAM,KAAK,SAAS,SAAS;AAC7B,+BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,+BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,+BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AACjC,+BAAS,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;AAEjC,8BAAQ,KAAK,IAAI,KAAK,GAAG,KAAK,CAAC;AAC/B,8BAAQ,KAAK,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAGnC,4BAAM,WAAW,KAAK,IAAI,GAAG,KAAK,KAAK,eAAe,CAAC,CAAC;AAGxD,0BAAI,MAAM,GAAG;AACX,8BAAMC,aAAY,SAAS,SAAS;AACpC,iCAAS,KAAK,IAAI,IAAI,CAAC;AACvB,iCAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,gCAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,mCAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,cAAc,KAAK,KAAK,IAAI,KAAK,IAAI,cAAc,CAAC;AAAA,wBAC3F;AACA,iCAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,kCAAQ,KAAKA,YAAWA,aAAY,IAAI,GAAGA,aAAY,IAAI,IAAI,CAAC;AAAA,wBAClE;AAAA,sBACF;AAGA,4BAAM,YAAY,SAAS,SAAS;AACpC,+BAAS,KAAK,IAAI,IAAI,CAAC;AACvB,+BAAS,IAAI,GAAG,KAAK,UAAU,KAAK;AAClC,8BAAM,QAAS,IAAI,WAAY,KAAK,KAAK;AACzC,iCAAS,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,cAAc,KAAK,KAAK,IAAI,KAAK,IAAI,cAAc,CAAC;AAAA,sBAC3F;AACA,+BAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,gCAAQ,KAAK,WAAW,YAAY,IAAI,GAAG,YAAY,IAAI,IAAI,CAAC;AAAA,sBAClE;AAAA,oBACF;AAAA,kBACF;AAAA,gBACF;AAAA,cACF;AAAA,YACF,SAAS,OAAO;AACd,sBAAQ,MAAM,uBAAuB,GAAG,oCAAW,KAAK;AAAA,YAC1D;AAAA,UACF;AAAA,QACF;AAAA,MACF,CAAC;AAGD,UAAI,QAAQ,SAAS,iBAAiB;AACpC,qBAAa,KAAK,EAAE,UAAU,iBAAiB,OAAO,iBAAiB,KAAK,QAAQ,OAAO,CAAC;AAAA,MAC9F;AAEA,YAAM,gBAAiB,SAAS,SAAS,IAAK;AAC9C,YAAM,iBAAkB,QAAQ,SAAS,KAAM,iBAAiB,IAAI,QAAQ,SAAS,IAAI,iBAAiB;AAC1G,cAAQ,IAAI,2CAAuB,aAAa,+BAAW,SAAS,SAAS,CAAC,kBAAQ,QAAQ,SAAS,CAAC,qBAAM;AAAA,IAChH;AAGA,YAAQ,IAAI,gBAAgBJ,SAAQ,KAAK,SAAS,SAAS,CAAC,kBAAQ,QAAQ,SAAS,CAAC,qBAAM;AAG5F,QAAI,SAAS,SAAS,GAAG;AACvB,UAAI,OAAO,UAAU,OAAO;AAC5B,UAAI,OAAO,UAAU,OAAO;AAC5B,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC3C,eAAO,KAAK,IAAI,MAAM,SAAS,CAAC,CAAC;AACjC,eAAO,KAAK,IAAI,MAAM,SAAS,CAAC,CAAC;AACjC,eAAO,KAAK,IAAI,MAAM,SAAS,IAAI,CAAC,CAAC;AACrC,eAAO,KAAK,IAAI,MAAM,SAAS,IAAI,CAAC,CAAC;AAAA,MACvC;AACA,cAAQ,IAAI,gBAAgBA,SAAQ,iCAAa,KAAK,QAAQ,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,MAAM;AAAA,IAC3I;AAEA,WAAO;AAAA,MACL,UAAU,IAAI,aAAa,QAAQ;AAAA,MACnC,iBAAiB,IAAI,YAAY,OAAO;AAAA,MACxC,cAAc,aAAa,OAAO,OAAM,EAAE,MAAM,EAAE,QAAS,CAAC;AAAA;AAAA,MAE5D,mBAAmB,qBAAqB,kBAAkB,SAAS,IAAI,oBAAoB;AAAA,IAC7F;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,0CAAsB,KAAK;AACzC,YAAQ,MAAM,0CAAsB,MAAM,KAAK;AAC/C,UAAM;AAAA,EACR;AACF;AAMA,SAAS,oBAAoB,iBAAiB;AAE5C,MAAI,OAAO,oBAAoB,YAAY,oBAAoB,MAAM;AACnE,QAAI,gBAAgB,cAAc;AAChC,aAAO,gBAAgB;AAAA,IACzB;AACA,sBAAkB,gBAAgB;AAAA,EACpC;AAEA,QAAMA,YAAW;AACjB,QAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,QAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,QAAM,gBAAgB,aAAa,YAAY;AAG/C,MAAI,kBAAkB,QAAQ;AAC5B,UAAM,YAAY,aAAa,YAAY;AAC3C,UAAM,YAAY,aAAa,YAAY;AAG3C,UAAM,gBAAgB,aAAa,QAAQ,YAAY,EAAE,EAAE,YAAY;AACvE,UAAM,mBAAmB,oBAAI,IAAI;AAAA,MAC/B;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACrB;AAAA,MAAO;AAAA,MAAO;AAAA,MAAO;AAAA,MACrB;AAAA,IACF,CAAC;AACD,QAAI,iBAAiB,IAAI,aAAa,KAAK,UAAU,KAAK,aAAa,KAAK,YAAY,KAAK,aAAa,GAAG;AAC3G,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,YAAY,KAC9B,UAAU,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,KAAK,CAAC,UAAU,SAAS,QAAQ,GAAI;AAChG,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,WAAW,GAAG;AACnC,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,WAAW,GAAG;AACnC,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,gBAAgB,GAAG;AACxC,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,gBAAgB,GAAG;AACxC,aAAO;AAAA,IACT;AAIA,UAAM,cAAc,UAAU,MAAM,+BAA+B;AACnE,QAAI,aAAa;AACf,YAAM,SAAS,SAAS,YAAY,CAAC,GAAG,EAAE;AAC1C,YAAM,gBAAgB,SAAS;AAC/B,aAAO,QAAQ;AAAA,IACjB;AAGA,QAAI,UAAU,SAAS,YAAY,KAAK,UAAU,SAAS,mBAAmB,GAAG;AAC/E,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,YAAY,KAAK,UAAU,SAAS,mBAAmB,GAAG;AAC/E,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,SAAS,GAAG;AACjC,aAAO;AAAA,IACT;AAIA,QAAI,iBAAiB,KAAK,SAAS,EAAG,QAAO;AAC7C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,gBAAgB,KAAK,SAAS,EAAG,QAAO;AAC5C,QAAI,gBAAgB,KAAK,SAAS,EAAG,QAAO;AAC5C,QAAI,eAAe,KAAK,SAAS,EAAG,QAAO;AAC3C,QAAI,eAAe,KAAK,SAAS,EAAG,QAAO;AAC3C,QAAI,aAAa,KAAK,SAAS,EAAG,QAAO;AACzC,QAAI,aAAa,KAAK,SAAS,EAAG,QAAO;AAEzC,QAAI,kBAAkB,KAAK,SAAS,GAAG;AACrC,YAAM,IAAI,UAAU,MAAM,gBAAgB;AAC1C,aAAO,SAAS,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI;AAAA,IACvC;AAGA,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAE3E,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAE3E,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAE3E,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAG3E,UAAM,WAAW,CAAC,YAAY,aAAa,SAAS,SAAS,MAAM;AACnE,eAAW,WAAW,UAAU;AAC9B,UAAI,aAAa,SAAS,OAAO,GAAG;AAElC,cAAM,eAAe,aAAa,QAAQ,OAAO;AACjD,eAAO,aAAa,UAAU,YAAY;AAAA,MAC5C;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,SAAS,EAAG,QAAO;AAC9C,MAAI,cAAc,KAAK,aAAa,KAAK,eAAe,KAAK,aAAa,EAAG,QAAO;AAGpF,MAAI,kBAAkB,QAAQ,kBAAkB,UAAU,kBAAkB,QAAQ;AAElF,QAAI,kBAAkB,YAAY,kBAAkB,OAAQ,QAAO;AACnE,QAAI,kBAAkB,WAAW,kBAAkB,MAAO,QAAO;AACjE,WAAO;AAAA,EACT;AACA,MAAI,kBAAkB,OAAQ,QAAO;AAGrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,OAAQ,QAAO;AACrC,MAAI,kBAAkB,QAAQ;AAG5B,UAAM,MAAM,aAAa,YAAY,GAAG;AACxC,UAAM,aAAa,MAAM,IAAI,aAAa,UAAU,GAAG,GAAG,IAAI,cAAc,YAAY;AACxF,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAEhC,WAAO;AAAA,EACT;AAGA,MAAI,kBAAkB,QAAQ;AAC5B,UAAM,YAAY,aAAa,YAAY;AAG3C,QAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,YAAM,cAAc,UAAU,MAAM,WAAW;AAC/C,UAAI,aAAa;AACf,cAAM,SAAS,SAAS,YAAY,CAAC,GAAG,EAAE;AAE1C,eAAO,SAAS,KAAK,QAAQ;AAAA,MAC/B,OAAO;AAEL,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,UAAU,WAAW,IAAI,GAAG;AAC9B,YAAM,cAAc,UAAU,MAAM,UAAU;AAC9C,UAAI,aAAa;AACf,cAAM,SAAS,SAAS,YAAY,CAAC,GAAG,EAAE;AAE1C,eAAO,SAAS,KAAK,QAAQ;AAAA,MAC/B,OAAO;AAEL,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,YAAM,cAAc,UAAU,MAAM,WAAW;AAC/C,UAAI,aAAa;AACf,cAAM,SAAS,SAAS,YAAY,CAAC,GAAG,EAAE;AAE1C,eAAO,SAAS,KAAK,QAAQ;AAAA,MAC/B,OAAO;AAEL,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,WAAW,IAAI,GAAG;AAC9B,aAAO;AAAA,IACT;AAGA,WAAO;AAAA,EACT;AAGA,MAAI,aAAa,KAAK,aAAa,KAAK,aAAa,KAAK,aAAa,GAAG;AACxE,WAAO;AAAA,EACT;AAGA,MAAI,kBAAkB,SAAS,aAAa,KAAK,aAAa,GAAG;AAC/D,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,KAAK,aAAa,GAAG;AACrC,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,cAAc,MAAM,aAAa;AACtD,MAAI,cAAc;AAEhB,UAAM,YAAY,cAAc,MAAM,iBAAiB;AACvD,QAAI,UAAW,QAAO,SAAS,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AAE5D,UAAM,SAAS,SAAS,aAAa,CAAC,GAAG,EAAE;AAC3C,WAAO,SAAS,SAAS;AAAA,EAC3B;AAGA,QAAM,gBAAgB,cAAc,MAAM,cAAc;AACxD,MAAI,eAAe;AACjB,UAAM,SAAS,SAAS,cAAc,CAAC,GAAG,EAAE;AAC5C,WAAO,OAAO;AAAA,EAChB;AAGA,QAAM,WAAW,aAAa,KAAK,aAAa,KAAK,kBAAkB;AACvE,MAAI,UAAU;AAEZ,QAAI,aAAa,KAAKA,SAAQ,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,QAAI,YAAY,KAAKA,SAAQ,GAAG;AAC9B,aAAO;AAAA,IACT;AAIA,WAAO;AAAA,EACT;AAGA,SAAO,cAAc,UAAU,CAAC;AAClC;AAKA,SAAS,cAAcA,WAAU;AAC/B,QAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,QAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,QAAM,gBAAgB,aAAa,YAAY;AAC/C,QAAM,MAAM,cAAc,UAAU,CAAC;AAErC,QAAM,iBAAiB;AAAA,IACrB,OAAO;AAAA,IAAG,OAAO;AAAA,IAAG,OAAO;AAAA,IAAG,OAAO;AAAA,IAAG,MAAM;AAAA,IAAG,MAAM;AAAA,IACvD,OAAO;AAAA,IAAG,OAAO;AAAA,IAAG,OAAO;AAAA,IAAK,OAAO;AAAA,IAAG,OAAO;AAAA,IACjD,QAAQ;AAAA,IAAK,WAAW;AAAA,IAAK,OAAO;AAAA,IAAI,QAAQ;AAAA,IAChD,OAAO;AAAA,IAAI,OAAO;AAAA,IAAI,MAAM;AAAA,IAAI,OAAO;AAAA,IAAM,OAAO;AAAA,IACpD,OAAO;AAAA,EACT;AAGA,MAAI,QAAQ,OAAO;AACjB,UAAM,YAAY,aAAa,YAAY;AAC3C,UAAM,YAAY,aAAa,YAAY;AAG3C,UAAM,gBAAgB,aAAa,QAAQ,YAAY,EAAE,EAAE,YAAY;AACvE,QAAI,eAAe,aAAa,MAAM,UAAa,UAAU,KAAK,aAAa,KAAK,YAAY,KAAK,aAAa,GAAG;AACnH,YAAM,QAAQ,eAAe,aAAa,MAAM,SAAY,eAAe,aAAa,IAAI;AAC5F,cAAQ,IAAI,gBAAMA,SAAQ,4BAAa,aAAa,sBAAsB,KAAK,EAAE;AACjF,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,YAAY,KAC9B,UAAU,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,KAAK,CAAC,UAAU,SAAS,QAAQ,GAAI;AAChG,cAAQ,IAAI,gBAAMA,SAAQ,oDAAqC;AAC/D,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,WAAW,GAAG;AACnC,cAAQ,IAAI,gBAAMA,SAAQ,mDAAoC;AAC9D,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,WAAW,GAAG;AACnC,cAAQ,IAAI,gBAAMA,SAAQ,qDAAsC;AAChE,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,gBAAgB,GAAG;AACxC,cAAQ,IAAI,gBAAMA,SAAQ,wDAAyC;AACnE,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,gBAAgB,GAAG;AACxC,cAAQ,IAAI,gBAAMA,SAAQ,wDAAyC;AACnE,aAAO;AAAA,IACT;AAIA,UAAM,cAAc,UAAU,MAAM,+BAA+B;AACnE,QAAI,aAAa;AACf,YAAM,SAAS,SAAS,YAAY,CAAC,GAAG,EAAE;AAC1C,YAAM,QAAQ,KAAK,IAAI,IAAI,SAAS,KAAK,GAAG;AAC5C,cAAQ,IAAI,gBAAMA,SAAQ,+BAAgB,SAAS,CAAC,YAAY,MAAM,YAAY,KAAK,EAAE;AACzF,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,YAAY,KAAK,UAAU,SAAS,mBAAmB,GAAG;AAC/E,cAAQ,IAAI,gBAAMA,SAAQ,oDAAqC;AAC/D,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,YAAY,KAAK,UAAU,SAAS,mBAAmB,GAAG;AAC/E,cAAQ,IAAI,gBAAMA,SAAQ,oDAAqC;AAC/D,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,SAAS,SAAS,GAAG;AACjC,cAAQ,IAAI,gBAAMA,SAAQ,iDAAkC;AAC5D,aAAO;AAAA,IACT;AAGA,QAAI,iBAAiB,KAAK,SAAS,EAAG,QAAO;AAC7C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,cAAc,KAAK,SAAS,EAAG,QAAO;AAC1C,QAAI,gBAAgB,KAAK,SAAS,EAAG,QAAO;AAC5C,QAAI,gBAAgB,KAAK,SAAS,EAAG,QAAO;AAC5C,QAAI,eAAe,KAAK,SAAS,EAAG,QAAO;AAC3C,QAAI,eAAe,KAAK,SAAS,EAAG,QAAO;AAC3C,QAAI,aAAa,KAAK,SAAS,EAAG,QAAO;AACzC,QAAI,aAAa,KAAK,SAAS,EAAG,QAAO;AACzC,QAAI,kBAAkB,KAAK,SAAS,GAAG;AACrC,YAAM,IAAI,UAAU,MAAM,gBAAgB;AAC1C,aAAO,KAAK,IAAI,IAAI,SAAS,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,GAAG;AAAA,IACnD;AAGA,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAE3E,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAE3E,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAE3E,QAAI,UAAU,WAAW,QAAQ,KAAK,UAAU,SAAS,QAAQ,EAAG,QAAO;AAG3E,YAAQ,IAAI,gBAAMA,SAAQ,wDAA0B;AACpD,WAAO;AAAA,EACT;AAGA,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,OAAO,EAAG,QAAO;AAC9E,MAAI,cAAc,SAAS,SAAS,EAAG,QAAO;AAG9C,QAAM,WAAW,cAAc,MAAM,aAAa,KAAK,cAAc,MAAM,cAAc;AACzF,MAAI,UAAU;AACZ,UAAM,SAAS,SAAS,SAAS,CAAC,GAAG,EAAE;AACvC,WAAO,MAAM;AAAA,EACf;AAIA,OAAK,QAAQ,SAAS,QAAQ,QAAQ,aAAa,YAAY,MAAM,UAAU;AAC7E,WAAO;AAAA,EACT;AACA,OAAK,QAAQ,SAAS,QAAQ,QAAQ,aAAa,YAAY,MAAM,SAAS;AAC5E,WAAO;AAAA,EACT;AACA,MAAI,eAAe,GAAG,MAAM,QAAW;AACrC,WAAO,eAAe,GAAG;AAAA,EAC3B;AAGA,QAAM,eAAe,cAAc,MAAM,iBAAiB;AAC1D,MAAI,gBAAgB,UAAU,KAAK,GAAG,GAAG;AACvC,WAAO,KAAK,IAAI,IAAI,SAAS,aAAa,CAAC,GAAG,EAAE,IAAI,KAAK,GAAG;AAAA,EAC9D;AAGA,QAAM,eAAe,IAAI,MAAM,WAAW;AAC1C,MAAI,cAAc;AAChB,UAAM,SAAS,SAAS,aAAa,CAAC,GAAG,EAAE;AAC3C,WAAO,KAAK,IAAI,IAAI,SAAS,KAAK,GAAG;AAAA,EACvC;AAGA,QAAM,gBAAgB,IAAI,MAAM,YAAY;AAC5C,MAAI,eAAe;AACjB,UAAM,SAAS,SAAS,cAAc,CAAC,GAAG,EAAE;AAC5C,WAAO,KAAK,IAAI,MAAM,SAAS,MAAM,IAAI;AAAA,EAC3C;AAGA,QAAM,iBAAiB,IAAI,MAAM,aAAa;AAC9C,MAAI,gBAAgB;AAClB,UAAM,SAAS,SAAS,eAAe,CAAC,GAAG,EAAE;AAC7C,WAAO,MAAM;AAAA,EACf;AAGA,QAAM,gBAAgB,IAAI,MAAM,YAAY;AAC5C,MAAI,eAAe;AACjB,UAAM,SAAS,SAAS,cAAc,CAAC,GAAG,EAAE;AAC5C,WAAO,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI;AAAA,EACzC;AAGA,QAAM,gBAAgB,IAAI,MAAM,YAAY;AAC5C,MAAI,eAAe;AACjB,UAAM,SAAS,SAAS,cAAc,CAAC,GAAG,EAAE;AAC5C,WAAO,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI;AAAA,EACzC;AAGA,QAAM,gBAAgB,IAAI,MAAM,YAAY;AAC5C,MAAI,eAAe;AACjB,UAAM,SAAS,SAAS,cAAc,CAAC,GAAG,EAAE;AAC5C,QAAI,WAAW,GAAG;AAEhB,aAAO;AAAA,IACT,OAAO;AAEL,aAAO,KAAK,IAAI,KAAK,SAAS,KAAK,IAAI;AAAA,IACzC;AAAA,EACF;AAGA,QAAM,WAAW,WAAW,KAAK,GAAG,KAAK,QAAQ;AACjD,MAAI,YAAY,aAAa,KAAKA,SAAQ,GAAG;AAC3C,YAAQ,IAAI,gBAAMA,SAAQ,qCAAsB;AAChD,WAAO;AAAA,EACT;AAGA,MAAI,YAAY,KAAKA,SAAQ,KAAK,UAAU;AAC1C,YAAQ,IAAI,gBAAMA,SAAQ,mCAAoB;AAC9C,WAAO;AAAA,EACT;AAGA,MAAI,UAAU;AACZ,YAAQ,IAAI,gBAAMA,SAAQ,0CAA2B;AACrD,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,OAAO;AACjB,UAAM,MAAM,aAAa,YAAY,GAAG;AACxC,UAAM,aAAa,MAAM,IAAI,aAAa,UAAU,GAAG,GAAG,IAAI,cAAc,YAAY;AACxF,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,QAAI,cAAc,MAAO,QAAO;AAChC,YAAQ,IAAI,gBAAMA,SAAQ,6CAA8B;AACxD,WAAO;AAAA,EACT;AAIA,MAAI,QAAQ,OAAO;AACjB,YAAQ,IAAI,gBAAMA,SAAQ,2FAA+B;AACzD,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,SAAS,QAAQ,OAAO,QAAQ,OAAO;AACjD,YAAQ,IAAI,gBAAMA,SAAQ,oCAAqB;AAC/C,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,OAAO;AACjB,YAAQ,IAAI,gBAAMA,SAAQ,qCAAsB;AAChD,WAAO;AAAA,EACT;AAGA,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAC1B,MAAI,QAAQ,MAAO,QAAO;AAG1B,MAAI,QAAQ,OAAO;AACjB,YAAQ,IAAI,gBAAMA,SAAQ,sEAA8B;AACxD,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAEA,SAAS,UAAU;AACjB,SAAO;AACP,wBAAsB,OAAO;AAC/B;AAEA,SAAS,SAAS;AAEhB,KAAG,WAAW,GAAK,GAAK,GAAK,CAAC;AAE9B,KAAG,MAAM,GAAG,mBAAmB,GAAG,kBAAkB;AAEpD,MAAI,OAAO,WAAW,EAAG;AAEzB,KAAG,WAAW,OAAO;AACrB,KAAG,wBAAwB,gBAAgB;AAG3C,KAAG,OAAO,GAAG,YAAY;AAEzB,QAAM,SAAS,OAAO,QAAQ,OAAO;AACrC,QAAM,SAAS;AAAA,IACb,QAAQ;AAAA,IAAQ;AAAA,IAAG;AAAA,IAAG;AAAA,IACtB;AAAA,IAAG;AAAA,IAAO;AAAA,IAAG;AAAA,IACb;AAAA,IAAG;AAAA,IAAG;AAAA,IAAG;AAAA,IACT;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAG;AAAA,EACrB;AACA,KAAG,iBAAiB,iBAAiB,OAAO,MAAM;AAMlD,QAAM,wBAAwB,CAAC,UAAU;AACvC,WAAO,MAAM,QAAQ,MAAM,YAAY,KACrC,MAAM,aAAa,KAAK,OAAK,KAAK,EAAE,aAAa,WAAY,EAAE,MAAM,EAAE,QAAS,CAAC;AAAA,EACrF;AAEA,QAAM,YAAY,CAAC,OAAO,WAAW,UAAU;AAC7C,QAAI,sBAAsB,KAAK,KAAK,oBAAoB,iBAAiB;AAEvE,SAAG,MAAM,GAAG,kBAAkB;AAC9B,SAAG,UAAU,OAAO,OAAO,OAAO,KAAK;AACvC,SAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;AAGzC,SAAG,iBAAiB,iBAAiB,OAAO,MAAM;AAElD,SAAG,WAAW,GAAG,cAAc,MAAM,YAAY;AACjD,SAAG,oBAAoB,kBAAkB,GAAG,GAAG,OAAO,OAAO,GAAG,CAAC;AACjE,SAAG,WAAW,GAAG,sBAAsB,MAAM,WAAW;AAExD,iBAAW,OAAO,MAAM,cAAc;AACpC,YAAI,CAAC,IAAK;AACV,cAAM,QAAS,IAAI,MAAM,IAAI;AAC7B,YAAI,SAAS,EAAG;AAChB,cAAM,MAAM,IAAI,aAAa,UAAU,IAAI;AAC3C,WAAG,YAAY,GAAG,QAAQ,KAAK,GAAI;AACnC,WAAG,aAAa,GAAG,WAAW,OAAO,GAAG,cAAc,IAAI,QAAQ,CAAC;AAAA,MACrE;AAGA,SAAG,UAAU,MAAM,MAAM,MAAM,IAAI;AACnC,SAAG,YAAY,GAAG,OAAO,GAAG,GAAI;AAChC,SAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI;AAEtC,SAAG,cAAc,GAAG,QAAQ;AAC5B,SAAG,UAAU,GAAG,SAAS;AAEzB,SAAG,WAAW,gBAAgB,MAAM,KAAK;AACzC,SAAG,UAAU,gBAAgB,KAAK;AAGlC,SAAG,iBAAiB,iBAAiB,OAAO,eAAe;AAC3D,SAAG,WAAW,GAAG,cAAc,gBAAgB;AAC/C,SAAG,oBAAoB,kBAAkB,GAAG,GAAG,OAAO,OAAO,GAAG,CAAC;AACjE,SAAG,WAAW,GAAG,sBAAsB,eAAe;AACtD,SAAG,aAAa,GAAG,WAAW,GAAG,GAAG,gBAAgB,CAAC;AAGrD,SAAG,iBAAiB,iBAAiB,OAAO,MAAM;AAClD;AAAA,IACF;AAGA,OAAG,MAAM,GAAG,kBAAkB;AAC9B,OAAG,YAAY,GAAG,UAAU,GAAG,GAAI;AACnC,OAAG,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;AAEzC,OAAG,cAAc,GAAG,QAAQ;AAC5B,OAAG,UAAU,GAAG,SAAS;AAEzB,OAAG,WAAW,gBAAgB,MAAM,KAAK;AACzC,OAAG,UAAU,gBAAgB,KAAK;AAElC,OAAG,WAAW,GAAG,cAAc,MAAM,YAAY;AACjD,OAAG,oBAAoB,kBAAkB,GAAG,GAAG,OAAO,OAAO,GAAG,CAAC;AACjE,OAAG,WAAW,GAAG,sBAAsB,MAAM,WAAW;AACxD,OAAG,aAAa,GAAG,WAAW,MAAM,aAAa,GAAG,cAAc,CAAC;AAAA,EACrE;AAYA,QAAM,iBAAiB,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AAChE,QAAM,cAAc,CAAC;AACrB,QAAM,eAAe,CAAC;AACtB,QAAM,gBAAgB,CAAC;AAGvB,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AAC3C,UAAM,QAAQ,OAAO,CAAC;AACtB,QAAI,CAAC,MAAM,WAAW,CAAC,MAAM,gBAAgB,CAAC,MAAM,YAAa;AAEjE,UAAM,cAAc,oBAAoB,KAAK;AAC7C,UAAM,QAAQ,MAAM,UAAU,SAAY,MAAM,QAAQ;AAKxD,UAAM,YAAY,MAAM,KAAK,YAAY;AACzC,UAAM,UAAU,WAAW,KAAK,SAAS;AACzC,UAAM,UAAU,WAAW,KAAK,SAAS;AACzC,UAAM,aAAa,UAAU,MAAM,YAAY;AAC/C,UAAM,QAAQ,cAAc,WAAW,CAAC,MAAM;AAC9C,UAAM,cAAc,WAAW,WAAW;AAC1C,QAAI,aAAa;AACf,oBAAc,KAAK,CAAC;AACpB;AAAA,IACF;AACA,QAAI,MAAM,eAAe,MAAM,YAAY;AACzC,mBAAa,KAAK,CAAC;AACnB;AAAA,IACF;AAGA,UAAM,aAAa,YAAY,KAAK,WAAW;AAC/C,UAAM,cAAc,eAAe,SAAS,WAAW,KAAK;AAC5D,QAAI,aAAa;AACf,kBAAY,KAAK,CAAC;AAClB;AAAA,IACF;AAEA,UAAM,gBAAgB,CAAC,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AAC7E,UAAM,kBAAkB,cAAc,SAAS,WAAW,KAAM,SAAS,KAAO,SAAS;AACzF,UAAM,iBAAkB,KAAK,IAAI,QAAQ,CAAC,IAAI,OAAS,KAAK,IAAI,QAAQ,CAAC,IAAI;AAC7E,UAAM,QAAQ;AAEd,QAAI,YAAY;AAChB,QAAI,gBAAgB;AAClB,kBAAY,CAAC,GAAG,qBAAqB,GAAG,mBAAmB;AAAA,IAC7D,WAAW,iBAAiB;AAC1B,kBAAY,CAAC,GAAG,KAAK,GAAG,GAAG;AAAA,IAC7B,OAAO;AACL,kBAAY,CAAC,GAAG,WAAW,GAAG,mBAAmB;AAAA,IACnD;AAEA,cAAU,OAAO,WAAW,KAAK;AAAA,EACnC;AAGA,aAAW,KAAK,YAAY,QAAQ,GAAG;AACrC,UAAM,QAAQ,OAAO,CAAC;AACtB,QAAI,CAAC,MAAM,WAAW,CAAC,MAAM,gBAAgB,CAAC,MAAM,YAAa;AACjE,UAAM,QAAQ;AACd,cAAU,OAAO,CAAC,GAAG,qBAAqB,GAAG,mBAAmB,GAAG,KAAK;AAAA,EAC1E;AAGA,aAAW,KAAK,aAAa,QAAQ,GAAG;AACtC,UAAM,QAAQ,OAAO,CAAC;AACtB,QAAI,CAAC,MAAM,WAAW,CAAC,MAAM,gBAAgB,CAAC,MAAM,YAAa;AACjE,UAAM,QAAQ;AACd,cAAU,OAAO,CAAC,GAAG,WAAW,GAAG,mBAAmB,GAAG,KAAK;AAAA,EAChE;AAGA,aAAW,KAAK,cAAc,QAAQ,GAAG;AACvC,UAAM,QAAQ,OAAO,CAAC;AACtB,QAAI,CAAC,MAAM,WAAW,CAAC,MAAM,gBAAgB,CAAC,MAAM,YAAa;AACjE,UAAM,QAAQ;AACd,cAAU,OAAO,CAAC,GAAG,WAAW,GAAG,mBAAmB,GAAG,KAAK;AAAA,EAChE;AAEA,KAAG,QAAQ,GAAG,YAAY;AAC5B;AAOA,SAAS,kBAAkBK,SAAQ;AACjC,SAAOA,QAAO,IAAI,CAAC,OAAO,mBAAmB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,OAAO,MAAM,UAAU,SAAY,MAAM,QAAQ;AAAA,IACjD,aAAa,oBAAoB,KAAK;AAAA,EACxC,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM;AAEjB,UAAM,YAAY,EAAE,YAAY,MAAM,aAAa;AACnD,UAAM,YAAY,EAAE,YAAY,MAAM,aAAa;AAGnD,QAAI,aAAa,WAAW;AAC1B,YAAM,OAAO,SAAS,UAAU,CAAC,GAAG,EAAE;AACtC,YAAM,OAAO,SAAS,UAAU,CAAC,GAAG,EAAE;AACtC,aAAO,OAAO;AAAA,IAChB;AAGA,QAAI,aAAa,WAAW;AAG1B,aAAO,EAAE,QAAQ,EAAE;AAAA,IACrB;AAGA,UAAM,YAAY,EAAE,QAAQ,EAAE;AAC9B,QAAI,cAAc,EAAG,QAAO;AAG5B,WAAO,EAAE,gBAAgB,EAAE;AAAA,EAC7B,CAAC;AACH;AAMA,SAAS,sBAAsBA,SAAQ;AAErC,QAAM,eAAe,kBAAkBA,OAAM;AAG7C,eAAa,QAAQ,CAAC,EAAE,OAAO,cAAc,GAAG,eAAe;AAC7D,UAAM,WAAW,UAAU,UAAU;AACrC,UAAM,MAAM,SAAS,QAAQ;AAG7B,UAAM,WAAW;AACjB,UAAM,QAAQ,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAGlC,kBAAc,IAAI,eAAe,QAAQ;AAEzC,YAAQ,IAAI,0CAAY,MAAM,IAAI,WAAW,MAAM,KAAK,mBAAmB,aAAa,gBAAgB,UAAU,WAAW,QAAQ,EAAE;AAAA,EACzI,CAAC;AACH;AAMA,SAAS,oBAAoBA,SAAQ;AAEnC,QAAM,WAAWA,QAAO,OAAO,WAAS;AACtC,UAAM,gBAAgB,MAAM,QAAQ,IAAI,MAAM,OAAO,EAAE,IAAI;AAC3D,UAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,WAAO,kBAAkB;AAAA,EAC3B,CAAC;AAED,MAAI,SAAS,WAAW,EAAG;AAG3B,QAAM,eAAe,CAAC;AACtB,aAAW,SAAS,UAAU;AAC5B,UAAM,gBAAgB,MAAM,QAAQ,IAAI,MAAM,OAAO,EAAE,IAAI;AAC3D,UAAM,YAAY,aAAa,YAAY;AAG3C,QAAI,SAAS;AACb,QAAI,SAAS;AAEb,QAAI,UAAU,WAAW,KAAK,GAAG;AAC/B,eAAS;AACT,YAAM,QAAQ,UAAU,MAAM,WAAW;AACzC,eAAS,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAAA,IAC5C,WAAW,UAAU,WAAW,KAAK,GAAG;AACtC,eAAS;AACT,YAAM,QAAQ,UAAU,MAAM,WAAW;AACzC,eAAS,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAAA,IAC5C,WAAW,UAAU,WAAW,IAAI,GAAG;AACrC,eAAS;AACT,YAAM,QAAQ,UAAU,MAAM,UAAU;AACxC,eAAS,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAAA,IAC5C,WAAW,UAAU,WAAW,KAAK,GAAG;AACtC,eAAS;AACT,YAAM,QAAQ,UAAU,MAAM,WAAW;AACzC,eAAS,QAAQ,SAAS,MAAM,CAAC,GAAG,EAAE,IAAI;AAAA,IAC5C,WAAW,UAAU,WAAW,KAAK,GAAG;AACtC,eAAS;AACT,eAAS;AAAA,IACX,WAAW,UAAU,WAAW,KAAK,GAAG;AACtC,eAAS;AACT,eAAS;AAAA,IACX,WAAW,UAAU,WAAW,IAAI,GAAG;AACrC,eAAS;AACT,eAAS;AAAA,IACX;AAEA,QAAI,QAAQ;AACV,UAAI,CAAC,aAAa,MAAM,GAAG;AACzB,qBAAa,MAAM,IAAI,CAAC;AAAA,MAC1B;AACA,mBAAa,MAAM,EAAE,KAAK,EAAE,OAAO,QAAQ,aAAa,CAAC;AAAA,IAC3D;AAAA,EACF;AAGA,aAAW,CAAC,QAAQ,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AAE1D,UAAM,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AAGxC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,EAAE,OAAO,OAAO,IAAI,MAAM,CAAC;AACjC,YAAM,aAAa,MAAM;AAEzB,UAAI,YAAY;AAChB,UAAI,QAAQ;AAEZ,cAAQ,QAAQ;AAAA,QACd,KAAK;AAEH,sBAAY;AACZ,kBAAQ;AACR;AAAA,QACF,KAAK;AAEH,cAAI,YAAY;AACd,wBAAY;AACZ,oBAAQ;AAAA,UACV,OAAO;AACL,wBAAY;AACZ,oBAAQ;AAAA,UACV;AACA;AAAA,QACF,KAAK;AAEH,cAAI,YAAY;AACd,wBAAY;AACZ,oBAAQ;AAAA,UACV,OAAO;AACL,wBAAY;AACZ,oBAAQ;AAAA,UACV;AACA;AAAA,QACF,KAAK;AAEH,cAAI,YAAY;AACd,wBAAY;AACZ,oBAAQ;AAAA,UACV,OAAO;AACL,wBAAY;AACZ,oBAAQ;AAAA,UACV;AACA;AAAA,QACF,KAAK;AAEH,sBAAY;AACZ,kBAAQ;AACR;AAAA,QACF,KAAK;AAEH,sBAAY;AACZ,kBAAQ;AACR;AAAA,QACF,KAAK;AAGH,sBAAY;AACZ,kBAAQ;AACR;AAAA,MACJ;AAEA,UAAI,UAAU,MAAM;AAElB,cAAM,QAAQ;AAEd,YAAI,WAAW;AACb,gBAAM,eAAe;AACrB,kBAAQ,IAAI,iCAAa,MAAM,IAAI,kBAAQ,MAAM,kBAAQ,MAAM,kBAAQ,SAAS,WAAW,KAAK,EAAE;AAAA,QACpG,OAAO;AACL,kBAAQ,IAAI,iCAAa,MAAM,IAAI,kBAAQ,MAAM,kBAAQ,MAAM,uDAAoB,KAAK,EAAE;AAAA,QAC5F;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAIA,UAAU,iBAAiB,UAAU,OAAO,MAAM;AAChD,QAAM,OAAO,EAAE,OAAO,MAAM,CAAC;AAC7B,MAAI,CAAC,KAAM;AAEX,aAAW,cAAc,KAAK,IAAI,OAAO,SAAS;AAClD,eAAa,KAAK,MAAM,KAAK,IAAI;AACjC,cAAY;AAEZ,MAAI,iBAAiB,CAAC;AACtB,QAAM,MAAM,MAAM,KAAK,KAAK,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AAEzD,MAAI;AACF,QAAI,QAAQ,QAAQ;AAClB,uBAAiB,MAAM,WAAW,IAAI;AAAA,IACxC,WAAW,QAAQ,QAAQ;AACzB,uBAAiB,MAAM,WAAW,IAAI;AAAA,IACxC,OAAO;AACL,uBAAiB,CAAC,IAAI;AAAA,IACxB;AAEA,QAAI,eAAe,WAAW,GAAG;AAC/B,iBAAW,0BAA0B,OAAO;AAC5C,kBAAY;AACZ;AAAA,IACF;AAGA,mBAAe,KAAK,CAAC,GAAG,MAAM,cAAc,EAAE,IAAI,IAAI,cAAc,EAAE,IAAI,CAAC;AAG3E,aAAS,CAAC;AACV,cAAU,YAAY;AACtB,kBAAc,MAAM;AACpB,iBAAa;AACb,qBAAiB,CAAC;AAClB,2BAAuB;AACvB,uBAAmB;AAGnB,WAAO;AAEP,QAAI,aAAa;AACjB,UAAM,gBAAgB,CAAC;AAGvB,aAAS,IAAI,GAAG,IAAI,eAAe,QAAQ,KAAK;AAC9C,YAAM,IAAI,eAAe,CAAC;AAC1B,iBAAW,WAAW,IAAE,CAAC,IAAI,eAAe,MAAM,KAAK,EAAE,IAAI,OAAO,SAAS;AAG7E,YAAM,UAAU,MAAM,EAAE,KAAK,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AAG1D,YAAM,OAAO,MAAM,EAAE,KAAK;AAI1B,8CAAwC,MAAM,EAAE,IAAI;AAGpD,YAAM,WAAW,aAAa,KAAK,OAAO,KAAK,YAAY;AAC3D,YAAM,YAAY,YAAY;AAC9B,YAAM,YAAY,YAAY;AAC9B,YAAM,YAAY,YAAY;AAC9B,YAAM,YAAY,YAAY,UAAU,YAAY,QAAQ,YAAY;AACxE,YAAM,YAAY,YAAY;AAC9B,UAAI,iBAAiB;AACrB,UAAI,aAAa;AACjB,UAAI,eAAe;AACnB,UAAI,kBAAkB;AAEtB,UAAI,UAAU;AAEZ,YAAI,aAAa,KAAK,EAAE,IAAI,GAAG;AAC7B,uBAAa;AACb,kBAAQ,IAAI,UAAU,EAAE,IAAI,gCAAiB;AAAA,QAC/C;AAIA,cAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,IAAI,EAAE,MAAM,GAAG,EAAE;AACjD,cAAM,SAAS,MAAM,KAAK,UAAQ,SAAS,KAAK,KAAK,KAAK,CAAC,CAAC;AAE5D,YAAI,QAAQ;AACV,2BAAiB;AACjB,yBAAe;AACf,cAAI,YAAY;AACd,oBAAQ,IAAI,UAAU,EAAE,IAAI,wGAAiD;AAAA,UAC/E,OAAO;AACL,oBAAQ,IAAI,kBAAQ,EAAE,IAAI,0GAAyC;AAAA,UACrE;AAAA,QACF,OAAO;AACL,kBAAQ,IAAI,YAAY,EAAE,IAAI,0FAAmC;AACjE,yBAAe;AAAA,QACjB;AAAA,MACF,WAAW,WAAW;AAEpB,gBAAQ,IAAI,YAAY,EAAE,IAAI,oEAA4B;AAC1D,uBAAe;AAAA,MACjB,WAAW,WAAW;AAEpB,gBAAQ,IAAI,YAAY,EAAE,IAAI,oEAA4B;AAC1D,uBAAe;AAAA,MACjB,WAAW,WAAW;AAEpB,gBAAQ,IAAI,YAAY,EAAE,IAAI,oEAA4B;AAC1D,uBAAe;AAAA,MACjB,WAAW,WAAW;AAEpB,gBAAQ,IAAI,kBAAQ,EAAE,IAAI,gFAA8B;AACxD,yBAAiB;AACjB,uBAAe;AAAA,MACjB,WAAW,WAAW;AAEpB,gBAAQ,IAAI,kBAAQ,EAAE,IAAI,gFAA8B;AACxD,uBAAe;AAAA,MACjB,OAAO;AAEL,cAAM,eAAe,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,EAAE,SAAS,OAAO;AAC9H,YAAI,cAAc;AAChB,kBAAQ,IAAI,YAAY,EAAE,IAAI,mFAAiC;AAC/D,yBAAe;AAAA,QACjB,OAAO;AAGL,gBAAM,qBAAqB,mBAAmB,KAAK,IAAI;AACvD,gBAAM,gBAAgB,SAAS,KAAK,IAAI;AACxC,cAAI,oBAAoB;AACtB,oBAAQ,IAAI,wBAAS,EAAE,IAAI,qFAAwC;AACnE,2BAAe;AAAA,UACjB,WAAW,eAAe;AACxB,oBAAQ,IAAI,8BAAU,EAAE,IAAI,kGAAiC;AAC7D,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,MACF;AAEA,UAAI;AACF,YAAI;AAGJ,cAAM,eAAe,EAAE,KAAK,MAAM,OAAO,EAAE,IAAI;AAC/C,cAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,cAAM,gBAAgB,aAAa,YAAY;AAG/C,cAAM,iBACJ,oBAAoB,EAAE,IAAI,MAAM,SAChC,kBAAkB,UAClB,kBAAkB,SAClB,aAAa,KAAK,aAAa,KAC/B,cAAc,SAAS,SAAS,KAChC,iBAAiB,KAAK,aAAa,KACnC,cAAc,SAAS,IAAI,KAC3B,cAAc,SAAS,OAAO,KAC9B,cAAc,WAAW,KAAK,KAC9B,cAAc,WAAW,KAAK;AAEhC,cAAM,aAAa;AAAA,UACjB,cAAc,iBAAiB,MAAM;AAAA;AAAA,QACvC;AAGA,YAAI,gBAAgB;AAClB,kBAAQ,IAAI,wBAAS,YAAY,kBAAkB,WAAW,YAAY,IAAI;AAAA,QAChF;AAEA,YAAI,cAAc;AAChB,iBAAO,MAAM,mBAAmB,MAAM,EAAE,IAAI;AAE5C,eAAK,CAAC,KAAK,YAAY,KAAK,SAAS,WAAW,MAAM,CAAC,gBAAgB;AACrE,oBAAQ,IAAI,kBAAQ,EAAE,IAAI,iFAAyC;AACnE,kBAAM,SAAS,IAAI,mBAAmB,UAAU;AAChD,mBAAO,OAAO,MAAM,IAAI;AACxB,oBAAQ,IAAI,8BAAU,EAAE,IAAI,KAAK,KAAK,SAAS,SAAS,CAAC,kBAAQ,KAAK,gBAAgB,SAAS,CAAC,qBAAM;AAAA,UACxG;AAAA,QACF,WAAW,iBAAiB;AAC1B,kBAAQ,IAAI,uDAAe,EAAE,IAAI,EAAE;AACnC,gBAAM,SAAS,IAAI,mBAAmB,UAAU;AAChD,iBAAO,OAAO,MAAM,IAAI;AACxB,kBAAQ,IAAI,8BAAU,EAAE,IAAI,KAAK,KAAK,SAAS,SAAS,CAAC,kBAAQ,KAAK,gBAAgB,SAAS,CAAC,qBAAM;AAAA,QACxG,OAAO;AACL,gBAAM,SAAS,IAAI,mBAAmB,UAAU;AAChD,iBAAO,OAAO,MAAM,IAAI;AAAA,QAC1B;AAGA,YAAI,CAAC,KAAK,YAAY,KAAK,SAAS,WAAW,GAAG;AAChD,kBAAQ,IAAI,kBAAQ,EAAE,IAAI,uCAAS;AACnC;AAAA,QACF;AAGA,YAAI,4BAA4B;AAChC,cAAM,mBAAmB,EAAE,KAAK,MAAM,OAAO,EAAE,IAAI;AACnD,cAAM,cAAc,MAAM,iBAAiB,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACxE,cAAM,kBAAkB,YAAY,UAAU,CAAC;AAE/C,cAAM,mBAAmB,oBAAoB,EAAE,IAAI;AACnD,cAAM,aAAa,CAAC,OAAO,OAAO,OAAO,KAAK,EAAE,SAAS,eAAe,KAAK,CAAC,OAAO,OAAO,OAAO,KAAK,EAAE,SAAS,gBAAgB;AAEnI,YAAI,cAAc,CAAC,cAAc;AAC/B,kBAAQ,IAAI,8BAAU,EAAE,IAAI,KAAK,eAAe,0EAAwB;AACxE,cAAI;AAEF,kBAAM,cAAc,QAAQ;AAC5B,kBAAM,mBAAmB,CAAC,gBAAgB,cAAc,OAAO,OAAO;AACtE,oBAAQ,MAAM,IAAI,SAAS;AACzB,oBAAM,UAAU,KAAK,KAAK,GAAG;AAC7B,kBAAI,CAAC,iBAAiB,KAAK,aAAW,QAAQ,SAAS,OAAO,CAAC,GAAG;AAChE,4BAAY,MAAM,SAAS,IAAI;AAAA,cACjC;AAAA,YACF;AAEA,kBAAM,cAAc,MAAM,mBAAmB,MAAM,EAAE,IAAI;AAGzD,oBAAQ,MAAM;AAEd,gBAAI,eAAe,YAAY,YAAY,YAAY,SAAS,SAAS,GAAG;AAC1E,0CAA4B,YAAY;AACxC,sBAAQ,IAAI,8BAAU,EAAE,IAAI,8BAAU,0BAA0B,SAAS,CAAC,yDAAY;AAAA,YACxF;AAAA,UACF,SAAS,KAAK;AACZ,oBAAQ,MAAM,QAAQ,IAAI,KAAK,OAAO;AACtC,oBAAQ,KAAK,8BAAU,EAAE,IAAI,+CAAsB,IAAI,OAAO;AAAA,UAChE;AAAA,QACF;AAGA,YAAI,OAAO,UAAU,OAAO,WAAW,OAAO,UAAU,OAAO;AAC/D,iBAAS,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,KAAK,GAAG;AAChD,gBAAM,IAAI,KAAK,SAAS,CAAC;AACzB,gBAAM,IAAI,KAAK,SAAS,IAAI,CAAC;AAC7B,iBAAO,KAAK,IAAI,MAAM,CAAC;AACvB,iBAAO,KAAK,IAAI,MAAM,CAAC;AACvB,iBAAO,KAAK,IAAI,MAAM,CAAC;AACvB,iBAAO,KAAK,IAAI,MAAM,CAAC;AAAA,QACzB;AACA,cAAM,SAAS,EAAE,MAAM,MAAM,MAAM,KAAK;AAIxC,cAAM,WAAW,UAAU,UAAU;AACrC,cAAM,MAAM,SAAS,QAAQ;AAC7B;AAGA,cAAM,eAAe,GAAG,aAAa;AACrC,WAAG,WAAW,GAAG,cAAc,YAAY;AAC3C,WAAG,WAAW,GAAG,cAAc,KAAK,UAAU,GAAG,WAAW;AAE5D,cAAM,cAAc,GAAG,aAAa;AACpC,WAAG,WAAW,GAAG,sBAAsB,WAAW;AAClD,WAAG,WAAW,GAAG,sBAAsB,KAAK,iBAAiB,GAAG,WAAW;AAI3E,YAAI,kCAAkC;AACtC,YAAI,2BAA2B;AAE7B,4CAAkC;AAAA,QACpC,WAAW,cAAc,cAAc;AAGrC,4CAAkC,KAAK;AAAA,QACzC,WAAW,kBAAkB,WAAW;AAEtC,4CAAkC,KAAK;AAAA,QACzC;AAEA,sBAAc,KAAK;AAAA,UACjB,MAAM,EAAE;AAAA,UACR,SAAS;AAAA,UACT,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAAA,UAC3B;AAAA;AAAA;AAAA,UAGA;AAAA,UACA;AAAA,UACA,aAAa,KAAK,gBAAgB;AAAA;AAAA,UAElC,cAAc,KAAK,gBAAgB;AAAA;AAAA,UAEnC,mBAAmB,KAAK,qBAAqB;AAAA,UAC7C,OAAO,cAAc,EAAE,IAAI;AAAA,UAC3B,aAAa;AAAA,UACb;AAAA,UACA;AAAA;AAAA,UAEA,aAAa;AAAA,QACf,CAAC;AAED,gBAAQ,IAAI,kBAAQ,EAAE,IAAI,KAAK,KAAK,SAAS,SAAS,CAAC,kBAAQ,KAAK,gBAAgB,SAAS,CAAC,qBAAM;AAAA,MAKtG,SAAS,OAAO;AACd,gBAAQ,MAAM,2CAAa,EAAE,IAAI,kBAAQ,MAAM,OAAO;AAAA,MAExD;AAGA,UAAI,IAAI,eAAe,SAAS,GAAG;AACjC,cAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,CAAC,CAAC;AAAA,MACrD;AAAA,IACF;AAEA,QAAI,cAAc,WAAW,GAAG;AAC5B,iBAAW,0BAA0B,OAAO;AAC5C,kBAAY;AACZ;AAAA,IACJ;AAGA,aAAS;AAGT,wBAAoB,MAAM;AAG1B,0BAAsB,MAAM;AAI5B,6BAAyB;AAGzB,UAAM,YAAY,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,cAAc,GAAG,CAAC;AACtE,eAAW,mBAAc,OAAO,MAAM,YAAY,KAAK,MAAM,SAAS,CAAC,UAAU,SAAS;AAC1F,gBAAY;AAGZ,oBAAgB;AAGhB,uBAAmB;AACnB,eAAW;AAAA,EAEb,SAAS,KAAK;AACZ,YAAQ,MAAM,GAAG;AACjB,eAAW,mBAAc,IAAI,SAAS,OAAO;AAC7C,gBAAY;AAAA,EACd;AACF,CAAC;AAED,SAAS,WAAW,KAAK,OAAO,IAAI;AAClC,SAAO,cAAc;AACrB,SAAO,YAAY,UAAU,IAAI;AACnC;AAEA,SAAS,aAAa,MAAM,MAAM;AAChC,WAAS,cAAc;AACvB,WAAS,eAAe,OAAO,MAAM,QAAQ,CAAC,IAAI;AAClD,WAAS,UAAU,IAAI,QAAQ;AACjC;AAEA,SAAS,kBAAkB;AACzB,YAAU,YAAY;AAGtB,QAAM,eAAe,kBAAkB,MAAM;AAG7C,UAAQ,IAAI,0FAAoB;AAChC,eAAa,QAAQ,CAAC,EAAE,OAAO,cAAc,GAAG,UAAU;AACxD,UAAM,cAAc,oBAAoB,KAAK;AAC7C,YAAQ,IAAI,KAAK,QAAQ,CAAC,KAAK,WAAW,WAAW,MAAM,SAAS,GAAG,mBAAmB,aAAa,GAAG;AAAA,EAC5G,CAAC;AAED,eAAa,QAAQ,CAAC,EAAE,OAAO,cAAc,GAAG,iBAAiB;AAC/D,UAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAK,YAAY,MAAM,UAAU,eAAe;AAEhD,UAAM,YAAY,SAAS,cAAc,MAAM;AAC/C,cAAU,YAAY;AACtB,cAAU,cAAc,eAAe;AAEvC,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,YAAY,gBAAgB,MAAM,UAAU,YAAY,EAAE;AACjE,WAAO,UAAU,CAAC,MAAM;AACtB,QAAE,gBAAgB;AAClB,kBAAY,aAAa;AAAA,IAC3B;AAEA,UAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,aAAS,YAAY;AACrB,QAAI,CAAC,MAAM,SAAS;AAClB,eAAS,MAAM,kBAAkB;AAAA,IACnC,OAAO;AACL,UAAI,eAAe,MAAM;AACzB,UAAI,cAAc,IAAI,aAAa,GAAG;AACpC,uBAAe,cAAc,IAAI,aAAa;AAAA,MAChD;AACA,eAAS,MAAM,kBAAkB;AAAA,IACnC;AAEA,UAAM,WAAW,SAAS,cAAc,KAAK;AAC7C,aAAS,YAAY;AACrB,QAAI,cAAc,oBAAoB,KAAK;AAE3C,QAAI,MAAM,YAAY;AACpB,oBAAc;AAAA,IAChB,WAES,MAAM,aAAa;AAC1B,YAAML,YAAW,MAAM;AACvB,UAAI,YAAY,KAAKA,SAAQ,GAAG;AAC9B,sBAAc;AAAA,MAChB,OAAO;AACL,sBAAc;AAAA,MAChB;AAAA,IACF;AACA,aAAS,cAAc;AACvB,aAAS,aAAa,SAAS,MAAM,IAAI;AAGzC,aAAS,iBAAiB,YAAY,CAAC,MAAM;AAC3C,QAAE,gBAAgB;AAClB,gBAAU,aAAa;AAAA,IACzB,CAAC;AAED,SAAK,YAAY,SAAS;AAC1B,SAAK,YAAY,MAAM;AACvB,SAAK,YAAY,QAAQ;AACzB,SAAK,YAAY,QAAQ;AACzB,cAAU,YAAY,IAAI;AAAA,EAC5B,CAAC;AAED,uBAAqB;AACvB;AAKA,SAAS,YAAY,OAAO;AAC1B,MAAI,QAAQ,KAAK,SAAS,OAAO,OAAQ;AAEzC,SAAO,KAAK,EAAE,UAAU,CAAC,OAAO,KAAK,EAAE;AAGvC,MAAI,YAAY;AACd,QAAI,OAAO,KAAK,EAAE,SAAS;AAEzB,UAAI,eAAe,QAAQ,KAAK,MAAM,IAAI;AACxC,uBAAe,KAAK,KAAK;AAGzB,YAAI,cAAc,IAAI,KAAK,GAAG;AAC5B,gBAAM,gBAAgB,cAAc,IAAI,KAAK;AAC7C,2BAAiB,OAAO,aAAa;AAAA,QACvC,OAAO;AACL,gBAAM,aAAa,cAAc;AACjC,gBAAM,WAAW,UAAU,UAAU;AACrC,wBAAc,IAAI,OAAO,QAAQ;AACjC,2BAAiB,OAAO,QAAQ;AAAA,QAClC;AAAA,MACF,OAAO;AACL,YAAI,cAAc,IAAI,KAAK,GAAG;AAC5B,gBAAM,gBAAgB,cAAc,IAAI,KAAK;AAC7C,2BAAiB,OAAO,aAAa;AAAA,QACvC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,kBAAgB;AAChB,qBAAmB;AACrB;AAKA,SAAS,UAAU,kBAAkB;AACnC,UAAQ,IAAI,wCAAU,gBAAgB,EAAE;AAGxC,eAAa;AAGb,gBAAc,MAAM;AACpB,mBAAiB,CAAC;AAGlB,SAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,UAAM,UAAU;AAAA,EAClB,CAAC;AAGD,SAAO,gBAAgB,EAAE,UAAU;AAGnC,iBAAe,KAAK,gBAAgB;AAGpC,yBAAuB;AAGvB,QAAM,WAAW,WAAW,CAAC;AAC7B,gBAAc,IAAI,kBAAkB,QAAQ;AAC5C,mBAAiB,kBAAkB,QAAQ;AAG3C,kBAAgB;AAChB,qBAAmB;AAEnB,UAAQ,IAAI,gBAAM,gBAAgB,oCAAW,QAAQ,EAAE;AACzD;AAKA,SAAS,iBAAiB,YAAY,UAAU;AAC9C,MAAI,aAAa,KAAK,cAAc,OAAO,OAAQ;AAEnD,QAAM,QAAQ,OAAO,UAAU;AAC/B,QAAM,MAAM,SAAS,QAAQ;AAG7B,QAAM,QAAQ,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAClC,QAAM,WAAW;AACnB;AAEA,eAAe,UAAU,MAAM;AAC7B,MAAI,OAAO,WAAW,EAAG;AACzB,QAAM,aAAa,OAAO,MAAM,OAAK,EAAE,OAAO;AAC9C,QAAM,aAAa,CAAC;AAGpB,MAAI,cAAc,CAAC,YAAY;AAE7B,UAAM,eAAe,OAAO,IAAI,CAAC,OAAO,SAAS,EAAE,OAAO,IAAI,EAAE,EAC7D,KAAK,CAAC,GAAG,OAAO,EAAE,MAAM,SAAS,QAAQ,EAAE,MAAM,SAAS,IAAI;AAGjE,qBAAiB,CAAC;AAGlB,QAAI,aAAa;AAGjB,iBAAa,QAAQ,CAAC,EAAE,OAAO,IAAI,MAAM;AACvC,YAAM,UAAU;AAGhB,UAAI,QAAQ,sBAAsB;AAChC,cAAM,WAAW,WAAW,CAAC;AAC7B,sBAAc,IAAI,KAAK,QAAQ;AAC/B,yBAAiB,KAAK,QAAQ;AAC9B,uBAAe,KAAK,GAAG;AAAA,MACzB,OAAO;AAEL,cAAM,WAAW,UAAU,UAAU;AACrC,sBAAc,IAAI,KAAK,QAAQ;AAC/B,yBAAiB,KAAK,QAAQ;AAC9B,uBAAe,KAAK,GAAG;AACvB;AAAA,MACF;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,gMAAqC;AAAA,EACnD,OAAO;AAEL,WAAO,QAAQ,WAAS;AACtB,YAAM,UAAU;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,kBAAgB;AAChB,qBAAmB;AACrB;AAEA,SAAS,uBAAuB;AAC9B,QAAM,aAAa,OAAO,MAAM,OAAK,EAAE,OAAO;AAC9C,MAAI,YAAY;AACd,2BAAuB,UAAU,IAAI,SAAS;AAAA,EAChD,OAAO;AACL,2BAAuB,UAAU,OAAO,SAAS;AAAA,EACnD;AACF;AAEA,eAAe,WAAW,MAAM;AAC9B,QAAM,QAAQ,CAAC;AACf,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,UAAU,IAAI;AACtC,eAAW,CAAC,UAAU,QAAQ,KAAK,OAAO,QAAQ,IAAI,KAAK,GAAG;AAC5D,UAAI,CAAC,SAAS,OAAO,aAAa,QAAQ,GAAG;AAC3C,cAAM,OAAO,MAAM,SAAS,MAAM,MAAM;AACxC,cAAM,KAAK,IAAI,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC;AAAA,MACvC;AAAA,IACF;AAAA,EACF,SAAS,GAAG;AACV,YAAQ,MAAM,CAAC;AACf,UAAM,IAAI,MAAM,iBAAiB;AAAA,EACnC;AACA,SAAO;AACT;AAEA,SAAS,aAAaA,WAAU;AAE9B,QAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,QAAM,gBAAgB,aAAa,YAAY;AAG/C,QAAM,kBAAkB,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,SAAS;AAC1F,aAAW,UAAU,iBAAiB;AACpC,QAAI,cAAc,SAAS,MAAM,KAAK,cAAc,SAAS,SAAS,GAAG,GAAG;AAC1E,aAAO;AAAA,IACT;AAAA,EACF;AAGA,MAAI,cAAc,KAAK,aAAa,KAAK,eAAe,KAAK,aAAa,GAAG;AAC3E,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB;AAAA,IACtB;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IACxC;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IACxC;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAO;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IACvD;AAAA,IAAQ;AAAA,IAAM;AAAA,IAAQ;AAAA;AAAA,IAEtB;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IACxC;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAQ;AAAA,EAC1C;AACA,QAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,SAAO,gBAAgB,SAAS,aAAa,KACtC,YAAY,KAAK,aAAa,KAC9B,aAAa,KAAK,aAAa,KAC/B,aAAa,KAAK,aAAa,KAC/B,aAAa,KAAK,aAAa,KAC/B,cAAc,KAAK,aAAa;AACzC;AAMA,SAAS,2BAA2B;AAElC,QAAM,cAAc,OAAO,OAAO,WAAS,MAAM,eAAe,MAAM,UAAU;AAEhF,MAAI,YAAY,WAAW,GAAG;AAC5B,YAAQ,IAAI,mFAAkB;AAC9B;AAAA,EACF;AAGA,QAAM,gBAAgB,CAAC,oBAAoB;AACzC,QAAI,OAAO,UAAU,OAAO,UAAU,OAAO,WAAW,OAAO;AAC/D,eAAW,KAAK,iBAAiB;AAC/B,UAAI,CAAC,KAAK,CAAC,EAAE,OAAQ;AACrB,aAAO,KAAK,IAAI,MAAM,EAAE,OAAO,IAAI;AACnC,aAAO,KAAK,IAAI,MAAM,EAAE,OAAO,IAAI;AACnC,aAAO,KAAK,IAAI,MAAM,EAAE,OAAO,IAAI;AACnC,aAAO,KAAK,IAAI,MAAM,EAAE,OAAO,IAAI;AAAA,IACrC;AACA,QAAI,CAAC,OAAO,SAAS,IAAI,KAAK,CAAC,OAAO,SAAS,IAAI,KAAK,CAAC,OAAO,SAAS,IAAI,KAAK,CAAC,OAAO,SAAS,IAAI,GAAG;AACxG,aAAO;AAAA,IACT;AACA,WAAO,EAAE,MAAM,MAAM,MAAM,KAAK;AAAA,EAClC;AAEA,QAAM,kBAAkB,CAAC,GAAG,IAAI,QAAQ;AAAA,IACtC,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO;AAAA,IACf,MAAM,EAAE,OAAO;AAAA,EACjB;AAEA,QAAM,kBAAkB,CAAC,GAAG,QAAQ;AAClC,UAAM,OAAO,KAAK,IAAI,GAAG,IAAI,OAAO,EAAE,IAAI;AAC1C,UAAM,QAAQ,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,IAAI;AAC3C,UAAM,SAAS,KAAK,IAAI,GAAG,IAAI,OAAO,EAAE,IAAI;AAC5C,UAAM,MAAM,KAAK,IAAI,GAAG,EAAE,OAAO,IAAI,IAAI;AACzC,UAAM,QAAQ,OAAO,QAAQ,SAAS;AACtC,WAAO,EAAE,MAAM,OAAO,QAAQ,KAAK,MAAM;AAAA,EAC3C;AAGA,QAAM,eAAe,OAAO,KAAK,WAAS;AACxC,UAAM,gBAAgB,MAAM,QAAQ,IAAI,MAAM,OAAO,EAAE,IAAI;AAC3D,UAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,UAAM,MAAM,cAAc,UAAU,CAAC;AACrC,UAAM,QAAQ,aAAa,YAAY;AAEvC,WAAO,QAAQ,SACR,QAAQ,QACR,WAAW,KAAK,GAAG,KACnB,MAAM,SAAS,UAAU,KACzB,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,OAAO,KACtB,MAAM,WAAW,KAAK;AAAA,EAC/B,CAAC;AAED,MAAI,gBAAgB,gBAAgB,aAAa,SAAS,aAAa,SAAS;AAChF,MAAI,sBAAsB,cAAc,WAAW;AAEnD,MAAI,CAAC,qBAAqB;AACxB,YAAQ,IAAI,gGAA0B;AACtC;AAAA,EACF;AAUA,QAAM,eAAe,CAAC,SAAS,oBAAoB,IAAI;AACvD,QAAM,eAAe,CAAC,MAAM,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI;AAKrE,MAAI,eAAe;AACjB,UAAM,WAAW,CAAC,OAAO,OAAO,OAAO,KAAK;AAC5C,QAAI,MAAM;AACV,eAAW,KAAK,UAAU;AACxB,YAAM,IAAI,OAAO,KAAK,OAAK,KAAK,EAAE,UAAU,CAAC,EAAE,eAAe,CAAC,EAAE,cAAc,aAAa,EAAE,IAAI,MAAM,CAAC;AACzG,UAAI,GAAG;AAAE,cAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,QAAQ,QAAQ,EAAE;AAAG;AAAA,MAAM;AAAA,IACtE;AACA,QAAI,CAAC,KAAK;AACR,YAAM,MAAM,OAAO,KAAK,OAAK,KAAK,EAAE,UAAU,CAAC,EAAE,eAAe,CAAC,EAAE,UAAU;AAC7E,UAAI,IAAK,OAAM,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAQ,QAAQ,MAAM;AAAA,IACrE;AACA,QAAI,OAAO,IAAI,QAAQ;AACrB,YAAM,cAAc,aAAa,aAAa;AAC9C,YAAM,UAAU,aAAa,IAAI,MAAM;AACvC,YAAM,QAAQ,UAAU,IAAK,cAAc,UAAW;AACtD,UAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACvC,gBAAQ,KAAK,mDAAqB,cAAc,QAAQ,SAAS,kBAAQ,YAAY,QAAQ,CAAC,CAAC,2CAAa,IAAI,MAAM,MAAM,IAAI,IAAI,iBAAO,QAAQ,QAAQ,CAAC,CAAC,aAAa,MAAM,QAAQ,CAAC,CAAC,mKAAsC;AAChO,wBAAgB;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACA,QAAM,wBAAwB,MAAM;AAClC,QAAI,cAAe,QAAO,EAAE,MAAM,aAAa,MAAM,QAAQ,eAAe,QAAQ,UAAU;AAC9F,UAAM,WAAW,CAAC,OAAO,OAAO,OAAO,KAAK;AAC5C,eAAW,KAAK,UAAU;AACxB,YAAM,IAAI,OAAO,KAAK,OAAK,KAAK,EAAE,UAAU,CAAC,EAAE,eAAe,CAAC,EAAE,cAAc,aAAa,EAAE,IAAI,MAAM,CAAC;AACzG,UAAI,EAAG,QAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,EAAE,QAAQ,QAAQ,EAAE;AAAA,IAC5D;AACA,UAAM,MAAM,OAAO,KAAK,OAAK,KAAK,EAAE,UAAU,CAAC,EAAE,eAAe,CAAC,EAAE,UAAU;AAC7E,QAAI,IAAK,QAAO,EAAE,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAQ,QAAQ,MAAM;AACpE,WAAO;AAAA,EACT;AACA,QAAM,sBAAsB,CAAC,UAAU;AACrC,QAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAClD,UAAM,YAAY;AAClB,UAAM,aAAa;AACnB,QAAI,QAAQ,GAAG;AACb,YAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,MAAM,KAAK,CAAC,CAAC;AACxD,YAAM,OAAO,IAAI;AACjB,YAAM,WAAW,QAAQ;AACzB,UAAI,SAAS,KAAK,YAAY,aAAa,YAAY,WAAY,QAAO;AAAA,IAC5E,WAAW,QAAQ,KAAK;AACtB,YAAM,MAAM,IAAI;AAChB,YAAM,QAAQ,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,MAAM,GAAG,CAAC,CAAC;AACtD,YAAM,OAAO;AACb,YAAM,WAAW,QAAQ;AACzB,UAAI,SAAS,KAAK,YAAY,aAAa,YAAY,WAAY,QAAO;AAAA,IAC5E;AACA,WAAO;AAAA,EACT;AAEA,QAAM,2BAA2B,CAAC,QAAQ,QAAQ,QAAQ;AACxD,UAAM,YAAa,WAAW;AAC9B,QAAI,WAAW;AACb,cAAQ,KAAK,4FAAsB,GAAG,YAAY,MAAM,gFAAe;AAAA,IACzE,OAAO;AACL,cAAQ,KAAK,4FAAsB,GAAG,aAAa,MAAM,YAAY,MAAM,gFAAe;AAAA,IAC5F;AACA,eAAW,MAAM,aAAa;AAC5B,UAAI,CAAC,GAAG,eAAe,CAAC,GAAG,qBAAqB,GAAG,kBAAkB,WAAW,EAAG;AACnF,YAAM,WAAW,GAAG;AACpB,YAAM,OAAO,GAAG;AAEhB,iBAAW,OAAO,MAAM;AACtB,YAAI,CAAC,OAAO,EAAE,IAAI,QAAQ,MAAM,IAAI,SAAS,KAAM;AACnD,cAAM,MAAM,IAAI,MAAM,MAAM,SAAS;AACrC,cAAM,MAAM,IAAI,MAAM,MAAM,SAAS;AACrC,cAAM,QAAQ,IAAI,QAAQ;AAC1B,cAAM,OAAO,IAAI,QAAQ,IAAI,SAAS;AACtC,iBAAS,IAAI,OAAO,IAAI,KAAK,KAAK,GAAG;AACnC,mBAAS,CAAC,KAAK;AACf,mBAAS,IAAI,CAAC,KAAK;AAAA,QACrB;AAEA,YAAI,MAAM,IAAI,MAAM,KAAK;AACzB,YAAI,MAAM,IAAI,MAAM,KAAK;AAAA,MAC3B;AAGA,SAAG,WAAW,GAAG,cAAc,GAAG,YAAY;AAC9C,SAAG,WAAW,GAAG,cAAc,UAAU,GAAG,WAAW;AAGvD,UAAI,OAAO,UAAU,OAAO,WAAW,OAAO,UAAU,OAAO;AAC/D,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC3C,cAAM,IAAI,SAAS,CAAC;AACpB,cAAM,IAAI,SAAS,IAAI,CAAC;AACxB,eAAO,KAAK,IAAI,MAAM,CAAC;AACvB,eAAO,KAAK,IAAI,MAAM,CAAC;AACvB,eAAO,KAAK,IAAI,MAAM,CAAC;AACvB,eAAO,KAAK,IAAI,MAAM,CAAC;AAAA,MACzB;AACA,SAAG,SAAS,EAAE,MAAM,MAAM,MAAM,KAAK;AAErC,UAAI,WAAW;AACb,gBAAQ,IAAI,qCAAY,GAAG,IAAI,qDAAkB,MAAM,EAAE;AAAA,MAC3D,OAAO;AACL,gBAAQ,IAAI,qCAAY,GAAG,IAAI,sDAAmB,MAAM,YAAY,MAAM,EAAE;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,sBAAsB;AAC1C,MAAI,eAAe,YAAY,QAAQ;AACrC,UAAM,YAAY,YAAY;AAC9B,UAAM,WAAW,UAAU,OAAO,UAAU;AAC5C,UAAM,YAAY,UAAU,OAAO,UAAU;AAC7C,UAAM,aAAa,oBAAoB,OAAO,oBAAoB;AAClE,UAAM,cAAc,oBAAoB,OAAO,oBAAoB;AAInE,UAAM,YAAY,YAAY,OAAO,CAAC,GAAG,OAAO,KAAM,MAAM,GAAG,oBAAqB,GAAG,kBAAkB,SAAS,IAAI,CAAC;AACvH,UAAM,gBAAgB,gBAAgB,qBAAqB,SAAS;AACpE,UAAM,UAAU,KAAK,IAAI,UAAU,SAAS;AAC5C,UAAM,QAAQ,cAAc,SAAS;AACrC,QAAI,OAAO;AACT,cAAQ,IAAI,uFAAgC,YAAY,MAAM,6BAAc,SAAS,YAAY,WAAW,QAAQ,CAAC,CAAC,YAAY,YAAY,QAAQ,CAAC,CAAC,iEAAyB;AAAA,IACnL,WAAW,YAAY,KAAK,YAAY,MAAM,cAAc,QAAQ,UAAU,KAAK;AAEjF,cAAQ,IAAI,0EAAwB,SAAS,+CAAiB,cAAc,MAAM,QAAQ,CAAC,CAAC,uEAA0B;AAAA,IACxH,OAAO;AAGP,YAAM,SAAS,aAAa,KAAK,WAAW,IAAI,aAAa,WAAW;AACxE,YAAM,SAAS,cAAc,KAAK,YAAY,IAAI,cAAc,YAAY;AAC5E,YAAM,SAAS,oBAAoB,MAAM;AACzC,YAAM,SAAS,oBAAoB,MAAM;AAGzC,WAAK,WAAW,KAAK,WAAW,MAAM,WAAW,QAAQ;AACvD,gBAAQ,KAAK,kGAAsC,MAAM,YAAY,MAAM,8IAA2B;AAAA,MACxG,WAAW,WAAW,KAAK,WAAW,GAAG;AACvC,gBAAQ,KAAK,6FAAsC,WAAW,QAAQ,CAAC,CAAC,WAAW,YAAY,QAAQ,CAAC,CAAC,SAAS,SAAS,QAAQ,CAAC,CAAC,SAAS,UAAU,QAAQ,CAAC,CAAC,WAAW,OAAO,QAAQ,CAAC,CAAC,WAAW,OAAO,QAAQ,CAAC,CAAC,QAAQ,YAAY,IAAI,IAAI,YAAY,MAAM,gCAAiB,MAAM,WAAW,MAAM,EAAE;AAClT,iCAAyB,QAAQ,QAAQ,mBAAmB,YAAY,MAAM,GAAG;AAEjF,8BAAsB,cAAc,WAAW,KAAK;AAAA,MACtD;AAAA,IACA;AAAA,EACF,OAAO;AACL,YAAQ,IAAI,0IAAiC;AAAA,EAC/C;AAEA,MAAI,iBAAiB;AACrB,MAAI,eAAe;AACjB,qBAAiB,gBAAgB,qBAAqB,aAAa;AACnE,YAAQ,IAAI,iDAAwB,aAAa,IAAI,EAAE;AACvD,YAAQ,IAAI,yDAAqC,oBAAoB,KAAK,QAAQ,CAAC,CAAC,UAAU,oBAAoB,KAAK,QAAQ,CAAC,CAAC,UAAU,oBAAoB,KAAK,QAAQ,CAAC,CAAC,UAAU,oBAAoB,KAAK,QAAQ,CAAC,CAAC,EAAE;AAC7N,YAAQ,IAAI,wDAAoC,cAAc,KAAK,QAAQ,CAAC,CAAC,UAAU,cAAc,KAAK,QAAQ,CAAC,CAAC,UAAU,cAAc,KAAK,QAAQ,CAAC,CAAC,UAAU,cAAc,KAAK,QAAQ,CAAC,CAAC,EAAE;AACpM,YAAQ,IAAI,qDAAiC,eAAe,KAAK,QAAQ,CAAC,CAAC,UAAU,eAAe,MAAM,QAAQ,CAAC,CAAC,WAAW,eAAe,OAAO,QAAQ,CAAC,CAAC,QAAQ,eAAe,IAAI,QAAQ,CAAC,CAAC,UAAU,eAAe,MAAM,QAAQ,CAAC,CAAC,OAAO;AAAA,EACtP,OAAO;AACL,YAAQ,IAAI,sKAAyC;AAAA,EACvD;AAGA,QAAM,yBAAyB,CAAC,IAAI,IAAI,QAAQ;AAC9C,YAAQ,IAAI,gFAAoB,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,IAAI;AAClF,eAAW,MAAM,aAAa;AAC5B,UAAI,CAAC,GAAG,YAAa;AAErB,YAAM,WAAW,GAAG;AACpB,eAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC3C,iBAAS,CAAC,KAAK;AACf,iBAAS,IAAI,CAAC,KAAK;AAAA,MACrB;AAEA,SAAG,WAAW,GAAG,cAAc,GAAG,YAAY;AAC9C,SAAG,WAAW,GAAG,cAAc,UAAU,GAAG,WAAW;AAEvD,UAAI,GAAG,QAAQ;AACb,WAAG,OAAO,QAAQ;AAClB,WAAG,OAAO,QAAQ;AAClB,WAAG,OAAO,QAAQ;AAClB,WAAG,OAAO,QAAQ;AAAA,MACpB;AAEA,cAAQ,IAAI,qCAAY,GAAG,IAAI,6CAAU;AAAA,IAC3C;AAEA,QAAI,eAAe;AACjB,YAAM,cAAc,gBAAgB,qBAAqB,IAAI,EAAE;AAC/D,YAAM,gBAAgB,gBAAgB,aAAa,aAAa;AAChE,cAAQ,IAAI,qDAAiC,cAAc,KAAK,QAAQ,CAAC,CAAC,UAAU,cAAc,MAAM,QAAQ,CAAC,CAAC,WAAW,cAAc,OAAO,QAAQ,CAAC,CAAC,QAAQ,cAAc,IAAI,QAAQ,CAAC,CAAC,UAAU,cAAc,MAAM,QAAQ,CAAC,CAAC,OAAO;AAAA,IACjP;AAEA,YAAQ,IAAI,iEAAe;AAAA,EAC7B;AAGA,MAAI,gBAAgB;AAMpB,MAAI,cAAc;AAClB,MAAI,iBAAiB;AACrB,MAAI,oBAAoB;AACxB,MAAI,cAAc;AAClB,MAAI,gBAAgB;AACpB,MAAI,mBAAmB;AACvB,MAAI,gBAAgB;AAEpB,aAAW,MAAM,aAAa;AAC5B,QAAI,CAAC,GAAG,YAAa;AACrB,UAAM,WAAW,yBAAyB,EAAE;AAC5C,QAAI,SAAS,SAAS,EAAG;AAEzB,UAAM,aAAa,GAAG,QAAQ,IAAI,YAAY;AAC9C,UAAM,aAAa,MAAM,KAAK,SAAS,IAAI,MAAS;AACpD,UAAM,cAAc,GAAG,aAAa,MAAQ;AAE5C,UAAM,cAAc,SAAS,OAAO,OAAK,KAAK,EAAE,SAAS,QAAQ;AACjE,UAAM,cAAc,YAAY;AAGhC,QAAI,eAAe,GAAG;AACpB,YAAM,QAAQ,cAAc,MAAO,aAAa;AAChD,UAAI,QAAQ,aAAa;AACvB,sBAAc;AACd,sBAAc;AACd,yBAAiB;AACjB,4BAAoB;AAAA,MACtB;AAAA,IACF,OAAO;AAEL,YAAM,QAAQ,SAAS,SAAS,aAAa;AAC7C,UAAI,QAAQ,eAAe;AACzB,wBAAgB;AAChB,wBAAgB;AAChB,2BAAmB;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,eAAe,eAAe;AACjC,kBAAc;AACd,qBAAiB;AACjB,yBAAqB,kBAAkB,CAAC,GAAG,OAAO,OAAK,KAAK,EAAE,SAAS,QAAQ,EAAE;AAAA,EACnF;AAEA,MAAI,CAAC,eAAe,CAAC,gBAAgB;AACnC,YAAQ,IAAI,sMAAsC;AAAA,EACpD,OAAO;AACL,YAAQ,IAAI,0EAAmB,YAAY,IAAI,cAAc,eAAe,MAAM,YAAY,iBAAiB,UAAU,CAAC,CAAC,YAAY,UAAU,GAAG;AAGpJ,UAAM,iBAAiB,OAAO,OAAO,WAAS,CAAC,MAAM,eAAe,CAAC,MAAM,UAAU;AACrF,UAAMM,gBAAe,CAAC,SAAS,oBAAoB,IAAI;AACvD,UAAM,kBAAkB,CAAC,OAAO,OAAO,OAAO,KAAK;AACnD,UAAM,gBAAgB,CAAC;AACvB,eAAW,KAAK,iBAAiB;AAC/B,iBAAW,KAAK,gBAAgB;AAC9B,YAAI,CAAC,KAAK,CAAC,EAAE,YAAa;AAC1B,YAAIA,cAAa,EAAE,IAAI,MAAM,EAAG,eAAc,KAAK,CAAC;AAAA,MACtD;AAAA,IACF;AAEA,QAAI,cAAc,WAAW,GAAG;AAC9B,cAAQ,IAAI,uLAA0D;AAAA,IACxE,OAAO;AACL,YAAM,yBAAyB,iCAAiC,gBAAgB,KAAK,IAAI,IAAI,eAAe,MAAM,CAAC;AACnH,UAAI,OAAO;AAEX,UAAI,aAAa;AACjB,iBAAW,YAAY,eAAe;AACpC,gBAAQ,IAAI,8DAAiB,SAAS,IAAI,EAAE;AAC5C,cAAM,SAAS,yBAAyB,QAAQ;AAChD,cAAM,YAAY,OAAO,OAAO,OAAK,KAAK,EAAE,SAAS,QAAQ;AAC7D,cAAM,cAAc,UAAU,UAAU,IAAI,YAAY;AACxD,YAAI,YAAY,SAAS,GAAG;AAC1B,kBAAQ,IAAI,oFAAmB,YAAY,MAAM,gCAAO;AACxD;AAAA,QACF;AAEA,cAAM,sBAAsB,iCAAiC,aAAa,KAAK,IAAI,IAAI,YAAY,MAAM,CAAC;AAG1G,cAAM,OAAO,uBAAuB,wBAAwB,qBAAqB,EAAE,SAAS,KAAK,UAAU,IAAI,UAAU,EAAE,CAAC;AAC5H,YAAI,CAAC,MAAM;AACT,kBAAQ,IAAI,+FAAoB;AAAA,QAClC,OAAO;AAEL,gBAAM,WAAW;AACjB,gBAAM,YAAY;AAClB,gBAAM,WAAW;AAGjB,gBAAM,OAAO,aAAa,gBAAgB,aAAa,KAAK,IAAI,KAAK,IAAI,SAAS;AAClF,cAAI,KAAK,cAAc,GAAG;AACxB,gBAAI,QAAQ,GAAG,QAAQ;AACvB,uBAAW,KAAK,KAAK,cAAc;AACjC,uBAAU,EAAE,SAAS,IAAI,EAAE,WAAW;AACtC,uBAAU,EAAE,SAAS,IAAI,EAAE,WAAW;AAAA,YACxC;AACA,kBAAM,YAAY,QAAQ,KAAK,aAAa;AAC5C,kBAAM,YAAY,QAAQ,KAAK,aAAa;AAE5C,kBAAM,SAAS,aAAa,gBAAgB,aAAa,WAAW,WAAW,QAAQ;AACvF,kBAAM,SAAS,aAAa,gBAAgB,aAAa,WAAW,WAAW,QAAQ;AAEvF,kBAAM,OAAO;AAAA,cACX,IAAI;AAAA,cACJ,IAAI;AAAA,cACJ,YAAY,OAAO;AAAA,cACnB,eAAe,OAAO;AAAA,cACtB,eAAe,OAAO;AAAA,cACtB,YAAY,OAAO;AAAA,cACnB,eAAe,OAAO;AAAA,cACtB,eAAe,OAAO;AAAA,cACtB,SAASA,cAAa,SAAS,IAAI;AAAA,cACnC,aAAc,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE,EAAEA,cAAa,SAAS,IAAI,CAAC,KAAK;AAAA,cACjF,cAAc,SAAS;AAAA,cACvB,QAAQ,YAAY,KAAK,OAAO,WAAW,KAAK,KAAK;AAAA,YACvD;AAEA,kBAAM,WAAW,CAAC,GAAG,MAAM;AACzB,kBAAI,CAAC,EAAG,QAAO;AAEf,kBAAI,EAAE,eAAe,EAAE,WAAY,QAAO,EAAE,aAAa,EAAE;AAC3D,kBAAI,EAAE,kBAAkB,EAAE,cAAe,QAAO,EAAE,gBAAgB,EAAE;AACpE,kBAAI,EAAE,eAAe,EAAE,WAAY,QAAO,EAAE,aAAa,EAAE;AAC3D,kBAAI,EAAE,kBAAkB,EAAE,cAAe,QAAO,EAAE,gBAAgB,EAAE;AAEpE,mBAAK,EAAE,eAAe,QAAQ,EAAE,eAAe,GAAI,SAAQ,EAAE,eAAe,MAAM,EAAE,eAAe;AAEnG,oBAAM,KAAK,KAAK,IAAI,EAAE,EAAE,IAAI,KAAK,IAAI,EAAE,EAAE;AACzC,oBAAM,KAAK,KAAK,IAAI,EAAE,EAAE,IAAI,KAAK,IAAI,EAAE,EAAE;AACzC,kBAAI,OAAO,GAAI,QAAO,KAAK;AAC3B,qBAAO;AAAA,YACT;AAEA,gBAAI,SAAS,MAAM,IAAI,GAAG;AACxB,2BAAa;AACb,qBAAO;AAAA,YACT,WAAW,SAAS,MAAM,UAAU,GAAG;AACrC,2BAAa;AAAA,YACf;AAEA,oBAAQ,IAAI,2DAAmB,KAAK,GAAG,QAAQ,CAAC,CAAC,OAAO,KAAK,GAAG,QAAQ,CAAC,CAAC,UAAU,KAAK,UAAU,UAAU,KAAK,UAAU,mBAAmB,KAAK,iBAAiB,GAAG,QAAQ,CAAC,CAAC,KAAK,KAAK,iBAAiB,GAAG,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG;AAAA,UAC/O,OAAO;AACL,oBAAQ,IAAI,6FAA4B,KAAK,UAAU,0BAAM;AAAA,UAC/D;AAAA,QACF;AAAA,MACF;AAKA,YAAM,cAAc,gBAAgB,UAAU;AAC9C,YAAM,aAAa,eAAe,KAC9B,IACA,eAAe,KACb,IACA,KAAK,IAAI,GAAG,KAAK,KAAK,cAAc,IAAI,CAAC;AAE/C,YAAM,YAAY,MAAM,cAAc;AACtC,YAAM,cAAc,YAAY,cAAc;AAG9C,YAAM,oBAAoB,gBAAgB,IAAI,OAAQ,aAAa,cAAc,KAAK,aAAa,KAAK,KAAK,cAAc,GAAG;AAC9H,UAAI,oBAAoB;AACxB,UAAI,CAAC,qBAAqB,cAAc,cAAc,eAAe,cAAc,GAAG;AACpF,cAAM,KAAK,MAAM,iBAAiB;AAClC,cAAM,KAAK,YAAY,iBAAiB;AACxC,4BAAqB,KAAK,OAAQ,KAAK,OAAQ,KAAK,KAAM;AAAA,MAC5D;AACA,UAAI,eAAe,qBAAqB;AAExC,UAAI,CAAC,gBAAgB,QAAQ,cAAc,cAAc,eAAe,cAAc,GAAG;AACvF,cAAM,QAAQ,KAAK,OAAO,KAAK,MAAM,MAAM,WAAW,MAAM,KAAK,KAAK,MAAM,MAAM,WAAW,MAAM,EAAE;AACrG,YAAI,QAAQ,MAAM;AAChB,yBAAe;AACf,kBAAQ,IAAI,+GAA+B,MAAM,QAAQ,CAAC,CAAC,mCAAU;AAAA,QACvE;AAAA,MACF;AAEA,UAAI,QAAQ,aAAa,cAAc,cAAc;AACnD,wBAAgB,EAAE,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,YAAY,WAAW,UAAU,KAAK,eAAe,UAAU,KAAK,eAAe,cAAc,KAAK,aAAa;AAC/J,gBAAQ,IAAI,iFAAqB,KAAK,YAAY,EAAE;AACpD,gBAAQ,IAAI,mFAAuB,cAAc,GAAG,QAAQ,CAAC,CAAC,SAAS,cAAc,GAAG,QAAQ,CAAC,CAAC,aAAa,SAAS,IAAI,WAAW,iBAAiB,WAAW,oBAAoB,KAAK,iBAAiB,GAAG,QAAQ,CAAC,CAAC,KAAK,KAAK,iBAAiB,GAAG,QAAQ,CAAC,CAAC,cAAc,KAAK,MAAM,GAAG;AAAA,MAChS,OAAO;AACL,cAAM,YAAY,MAAM,cAAc;AACtC,gBAAQ,IAAI,2JAAwC,SAAS,IAAI,WAAW,SAAS,UAAU,kBAAkB,WAAW,kBAAkB,YAAY,eAAe,SAAS,kDAAU;AAAA,MAC9L;AAAA,IACF;AAAA,EACF;AAGA,QAAM,mBAAmB;AACzB,MAAI,SAAS;AAEb,MAAI,eAAe;AACjB,UAAM,KAAK,cAAc;AACzB,UAAM,KAAK,cAAc;AAGzB,QAAI,KAAK,IAAI,EAAE,IAAI,oBAAoB,KAAK,IAAI,EAAE,IAAI,kBAAkB;AAEtE,cAAQ,IAAI,qHAA2B,gBAAgB,aAAa,cAAc,UAAU,aAAa,cAAc,YAAY,GAAG,QAAQ,CAAC,CAAC,kDAAe;AAC/J;AAAA,IACF,WAAW,iBAAiB,gBAAgB;AAC1C,YAAM,cAAc,gBAAgB,qBAAqB,IAAI,EAAE;AAC/D,YAAM,gBAAgB,gBAAgB,aAAa,aAAa;AAChE,YAAM,cAAc,eAAe,QAAQ,cAAc;AAEzD,UAAI,cAAc,OAAO,cAAc,SAAS,eAAe,QAAQ,KAAK;AAC1E,iBAAS,EAAE,IAAI,IAAI,KAAK,0BAA0B,YAAY,QAAQ,CAAC,CAAC,MAAM;AAAA,MAChF,OAAO;AACL,gBAAQ,KAAK,gIAAiC,eAAe,MAAM,QAAQ,CAAC,CAAC,UAAU,cAAc,MAAM,QAAQ,CAAC,CAAC,2DAAc;AAAA,MACrI;AAAA,IACF,OAAO;AAEL,eAAS,EAAE,IAAI,IAAI,KAAK,mCAAmC;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,CAAC,UAAU,iBAAiB,gBAAgB;AAE9C,QAAI,eAAe,SAAS,KAAK;AAC/B,cAAQ,IAAI,iIAA6B;AAAA,IAC3C,OAAO;AACL,YAAM,gBAAgB,oBAAoB,OAAO,oBAAoB,QAAQ;AAC7E,YAAM,gBAAgB,oBAAoB,OAAO,oBAAoB,QAAQ;AAC7E,YAAM,kBAAkB,cAAc,OAAO,cAAc,QAAQ;AACnE,YAAM,kBAAkB,cAAc,OAAO,cAAc,QAAQ;AAEnE,YAAM,cAAc;AAAA,QAClB;AAAA,QACA,cAAc,OAAO,oBAAoB;AAAA,QACzC,cAAc,OAAO,oBAAoB;AAAA,QACzC,iBAAiB;AAAA,MACnB;AACA,YAAM,cAAc;AAAA,QAClB;AAAA,QACA,cAAc,OAAO,oBAAoB;AAAA,QACzC,cAAc,OAAO,oBAAoB;AAAA,QACzC,iBAAiB;AAAA,MACnB;AAEA,UAAI,OAAO,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,UAAU,OAAO,SAAS;AAC5D,iBAAW,MAAM,aAAa;AAC5B,mBAAW,MAAM,aAAa;AAC5B,gBAAM,cAAc,gBAAgB,qBAAqB,IAAI,EAAE;AAC/D,gBAAM,KAAK,gBAAgB,aAAa,aAAa;AACrD,gBAAM,QAAQ,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE;AACxC,cAAI,GAAG,QAAQ,KAAK,QAAQ,QAAS,KAAK,IAAI,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,QAAQ,KAAK,OAAQ;AAClG,mBAAO,EAAE,IAAI,IAAI,OAAO,GAAG,OAAO,MAAM;AAAA,UAC1C;AAAA,QACF;AAAA,MACF;AAEA,YAAM,cAAc,eAAe,QAAQ,KAAK;AAChD,cAAQ,IAAI,yEAAuB,KAAK,GAAG,QAAQ,CAAC,CAAC,SAAS,KAAK,GAAG,QAAQ,CAAC,CAAC,qBAAqB,KAAK,MAAM,QAAQ,CAAC,CAAC,eAAe,YAAY,QAAQ,CAAC,CAAC,KAAK;AAEpK,WAAK,KAAK,IAAI,KAAK,EAAE,KAAK,oBAAoB,KAAK,IAAI,KAAK,EAAE,KAAK,sBAAsB,cAAc,OAAO,KAAK,SAAS,eAAe,QAAQ,MAAM;AACvJ,iBAAS,EAAE,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,wBAAwB,YAAY,QAAQ,CAAC,CAAC,MAAM;AAAA,MAChG,OAAO;AACL,gBAAQ,IAAI,sHAA4B;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,UAAU,CAAC,eAAe;AAC7B,UAAM,MAAM,sBAAsB;AAClC,QAAI,OAAO,IAAI,QAAQ;AACrB,YAAM,oBAAoB,gBAAgB,qBAAqB,IAAI,MAAM;AACzE,UAAI,kBAAkB,QAAQ,KAAK;AACjC,cAAM,gBAAgB,oBAAoB,OAAO,oBAAoB,QAAQ;AAC7E,cAAM,gBAAgB,oBAAoB,OAAO,oBAAoB,QAAQ;AAC7E,cAAM,cAAc,IAAI,OAAO,OAAO,IAAI,OAAO,QAAQ;AACzD,cAAM,cAAc,IAAI,OAAO,OAAO,IAAI,OAAO,QAAQ;AAEzD,cAAM,cAAc;AAAA,UAClB;AAAA,UACA,IAAI,OAAO,OAAO,oBAAoB;AAAA,UACtC,IAAI,OAAO,OAAO,oBAAoB;AAAA,UACtC,aAAa;AAAA,QACf;AACA,cAAM,cAAc;AAAA,UAClB;AAAA,UACA,IAAI,OAAO,OAAO,oBAAoB;AAAA,UACtC,IAAI,OAAO,OAAO,oBAAoB;AAAA,UACtC,aAAa;AAAA,QACf;AAEA,YAAI,OAAO,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,UAAU,OAAO,SAAS;AAC5D,mBAAW,MAAM,aAAa;AAC5B,qBAAW,MAAM,aAAa;AAC5B,kBAAM,cAAc,gBAAgB,qBAAqB,IAAI,EAAE;AAC/D,kBAAM,KAAK,gBAAgB,aAAa,IAAI,MAAM;AAClD,kBAAM,QAAQ,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE;AACxC,gBAAI,GAAG,QAAQ,KAAK,QAAQ,QAAS,KAAK,IAAI,GAAG,QAAQ,KAAK,KAAK,IAAI,QAAQ,QAAQ,KAAK,OAAQ;AAClG,qBAAO,EAAE,IAAI,IAAI,OAAO,GAAG,OAAO,MAAM;AAAA,YAC1C;AAAA,UACF;AAAA,QACF;AAEA,cAAM,cAAc,kBAAkB,QAAQ,KAAK;AACnD,aAAK,KAAK,IAAI,KAAK,EAAE,KAAK,oBAAoB,KAAK,IAAI,KAAK,EAAE,KAAK,sBAAsB,cAAc,OAAO,KAAK,SAAS,kBAAkB,QAAQ,MAAM;AAC1J,kBAAQ,KAAK,kGAAiC,IAAI,IAAI,IAAI,IAAI,MAAM,SAAS,KAAK,GAAG,QAAQ,CAAC,CAAC,OAAO,KAAK,GAAG,QAAQ,CAAC,CAAC,YAAY,YAAY,QAAQ,CAAC,CAAC,IAAI;AAC9J,mBAAS,EAAE,IAAI,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,gBAAgB,IAAI,MAAM,aAAa,YAAY,QAAQ,CAAC,CAAC,MAAM;AAAA,QAC/G;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ;AACX,YAAQ,IAAI,qGAAqB;AACjC;AAAA,EACF;AAEA,yBAAuB,OAAO,IAAI,OAAO,IAAI,OAAO,GAAG;AACzD;AAKA,SAAS,yBAAyB,OAAO;AACvC,MAAI,CAAC,MAAM,YAAa,QAAO,CAAC;AAEhC,QAAM,WAAW,CAAC;AAClB,QAAM,WAAW,MAAM;AAIvB,QAAM,mBAAmB,oBAAI,IAAI;AAEjC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC3C,UAAM,IAAI,SAAS,CAAC;AACpB,UAAM,IAAI,SAAS,IAAI,CAAC;AAGxB,UAAM,MAAM,GAAG,KAAK,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,KAAK,MAAM,IAAI,GAAG,IAAI,GAAG;AACrE,qBAAiB,IAAI,MAAM,iBAAiB,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAChE;AAIA,QAAM,kBAAkB;AACxB,mBAAiB,QAAQ,CAAC,OAAO,QAAQ;AACvC,QAAI,SAAS,iBAAiB;AAC5B,YAAM,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM;AACxC,eAAS,KAAK,EAAE,GAAG,GAAG,MAAM,UAAU,MAAa,CAAC;AAAA,IACtD;AAAA,EACF,CAAC;AAED,UAAQ,IAAI,oCAAW,MAAM,IAAI,mBAAS,SAAS,MAAM,uDAAe,eAAe,GAAG;AAG1F,MAAI,SAAS,SAAS,GAAG;AACvB,aAAS,MAAM,GAAG,CAAC,EAAE,QAAQ,CAAC,GAAG,QAAQ;AACvC,cAAQ,IAAI,iBAAO,MAAM,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,QAAQ,CAAC,CAAC,+BAAW,EAAE,KAAK,EAAE;AAAA,IACvF,CAAC;AAAA,EACH;AAGA,MAAI,SAAS,WAAW,GAAG;AACzB,YAAQ,IAAI,oCAAW,MAAM,IAAI,mFAAkB;AACnD,UAAM,eAAe,oBAAI,IAAI;AAC7B,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK,GAAG;AAC3C,YAAM,IAAI,KAAK,MAAM,SAAS,CAAC,IAAI,GAAG,IAAI;AAC1C,YAAM,IAAI,KAAK,MAAM,SAAS,IAAI,CAAC,IAAI,GAAG,IAAI;AAC9C,mBAAa,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE;AAAA,IAC9B;AAEA,iBAAa,QAAQ,SAAO;AAC1B,YAAM,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,MAAM;AACxC,eAAS,KAAK,EAAE,GAAG,GAAG,MAAM,QAAQ,CAAC;AAAA,IACvC,CAAC;AACD,YAAQ,IAAI,oCAAW,MAAM,IAAI,yBAAU,SAAS,MAAM,2BAAO;AAAA,EACnE;AAEA,SAAO;AACT;AAKA,SAAS,iCAAiC,UAAU,UAAU;AAC5D,MAAI,SAAS,UAAU,UAAU;AAC/B,WAAO;AAAA,EACT;AAGA,MAAI,OAAO;AACX,MAAI,OAAO;AACX,MAAI,OAAO;AACX,MAAI,OAAO;AAEX,aAAW,WAAW,UAAU;AAC9B,WAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,WAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,WAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,WAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAAA,EACjC;AAEA,QAAM,SAAS,OAAO;AACtB,QAAM,SAAS,OAAO;AAGtB,MAAI,SAAS,QAAS,SAAS,MAAO;AACpC,aAAS,KAAK,CAAC,GAAG,MAAM;AACtB,UAAI,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,MAAQ;AAChC,eAAO,EAAE,IAAI,EAAE;AAAA,MACjB;AACA,aAAO,EAAE,IAAI,EAAE;AAAA,IACjB,CAAC;AACD,WAAO,SAAS,MAAM,GAAG,QAAQ;AAAA,EACnC;AAEA,QAAM,mBAAmB,CAAC;AAC1B,QAAM,eAAe,oBAAI,IAAI;AAG7B,QAAM,mBAAmB;AAEzB,QAAM,UAAU;AAAA,IACd,EAAE,MAAM,sBAAO,MAAM,MAAM,MAAM,OAAO,SAAS,kBAAkB,MAAM,MAAM,MAAM,OAAO,SAAS,iBAAiB;AAAA,IACtH,EAAE,MAAM,sBAAO,MAAM,OAAO,SAAS,kBAAkB,MAAM,MAAM,MAAM,MAAM,MAAM,OAAO,SAAS,iBAAiB;AAAA,IACtH,EAAE,MAAM,sBAAO,MAAM,MAAM,MAAM,OAAO,SAAS,kBAAkB,MAAM,OAAO,SAAS,kBAAkB,MAAM,KAAK;AAAA,IACtH,EAAE,MAAM,sBAAO,MAAM,OAAO,SAAS,kBAAkB,MAAM,MAAM,MAAM,OAAO,SAAS,kBAAkB,MAAM,KAAK;AAAA,EACxH;AAGA,aAAW,UAAU,SAAS;AAC5B,UAAM,iBAAiB,SAAS;AAAA,MAAO,OACrC,EAAE,KAAK,OAAO,QAAQ,EAAE,KAAK,OAAO,QACpC,EAAE,KAAK,OAAO,QAAQ,EAAE,KAAK,OAAO,QACpC,CAAC,aAAa,IAAI,CAAC;AAAA,IACrB;AAEA,QAAI,eAAe,SAAS,GAAG;AAE7B,UAAI,eAAe;AACnB,UAAI,OAAO,SAAS,sBAAO;AACzB,wBAAgB;AAChB,wBAAgB;AAAA,MAClB,WAAW,OAAO,SAAS,sBAAO;AAChC,wBAAgB;AAChB,wBAAgB;AAAA,MAClB,WAAW,OAAO,SAAS,sBAAO;AAChC,wBAAgB;AAChB,wBAAgB;AAAA,MAClB,OAAO;AACL,wBAAgB;AAChB,wBAAgB;AAAA,MAClB;AAGA,YAAM,iBAAiB,eAAe,OAAO,OAAK,EAAE,SAAS,QAAQ;AAGrE,UAAI,iBAAiB;AACrB,UAAI,cAAc;AAElB,YAAM,kBAAkB,eAAe,SAAS,IAAI,iBAAiB;AACrE,iBAAW,WAAW,iBAAiB;AACrC,cAAM,WAAW,KAAK;AAAA,UACpB,KAAK,IAAI,QAAQ,IAAI,eAAe,CAAC,IAAI,KAAK,IAAI,QAAQ,IAAI,eAAe,CAAC;AAAA,QAChF;AACA,YAAI,WAAW,aAAa;AAC1B,wBAAc;AACd,2BAAiB;AAAA,QACnB;AAAA,MACF;AAEA,UAAI,gBAAgB;AAClB,yBAAiB,KAAK,cAAc;AACpC,qBAAa,IAAI,cAAc;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI,yEAAkB,iBAAiB,MAAM,2BAAO;AAG5D,MAAI,iBAAiB,SAAS,UAAU;AACtC,UAAM,oBAAoB,SAAS,OAAO,OAAK,CAAC,aAAa,IAAI,CAAC,CAAC;AAEnE,QAAI,kBAAkB,SAAS,GAAG;AAEhC,YAAM,WAAW,KAAK,KAAK,KAAK,KAAK,WAAW,iBAAiB,MAAM,CAAC;AACxE,YAAM,YAAY,SAAS;AAC3B,YAAM,aAAa,SAAS;AAE5B,YAAM,OAAO,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,IAAI,MAAM,MAAM,QAAQ,EAAE,KAAK,IAAI,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC;AAE1F,iBAAW,WAAW,mBAAmB;AACvC,cAAM,QAAQ,KAAK,IAAI,KAAK,OAAO,QAAQ,IAAI,QAAQ,SAAS,GAAG,WAAW,CAAC;AAC/E,cAAM,QAAQ,KAAK,IAAI,KAAK,OAAO,QAAQ,IAAI,QAAQ,UAAU,GAAG,WAAW,CAAC;AAChF,aAAK,KAAK,EAAE,KAAK,EAAE,KAAK,OAAO;AAAA,MACjC;AAEA,eAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,iBAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,gBAAM,eAAe,KAAK,CAAC,EAAE,CAAC;AAC9B,cAAI,aAAa,SAAS,GAAG;AAC3B,kBAAM,cAAc,QAAQ,IAAI,OAAO;AACvC,kBAAM,cAAc,QAAQ,IAAI,OAAO;AAEvC,gBAAI,iBAAiB,aAAa,CAAC;AACnC,gBAAI,cAAc;AAElB,uBAAW,WAAW,cAAc;AAClC,oBAAM,WAAW,KAAK;AAAA,gBACpB,KAAK,IAAI,QAAQ,IAAI,aAAa,CAAC,IAAI,KAAK,IAAI,QAAQ,IAAI,aAAa,CAAC;AAAA,cAC5E;AACA,kBAAI,WAAW,aAAa;AAC1B,8BAAc;AACd,iCAAiB;AAAA,cACnB;AAAA,YACF;AAEA,6BAAiB,KAAK,cAAc;AACpC,yBAAa,IAAI,cAAc;AAE/B,gBAAI,iBAAiB,UAAU,UAAU;AACvC;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,YAAI,iBAAiB,UAAU,UAAU;AACvC;AAAA,QACF;AAAA,MACF;AAEA,cAAQ,IAAI,+EAAmB,iBAAiB,UAAU,iBAAiB,SAAS,IAAI,IAAI,iBAAiB,OAAO,2BAAO;AAAA,IAC7H;AAAA,EACF;AAEA,UAAQ,IAAI,uDAAe,SAAS,MAAM,+CAAY,iBAAiB,MAAM,SAAI;AAEjF,SAAO;AACT;AAwGA,SAAS,aAAa,eAAe,aAAa,SAAS,SAAS,WAAW;AAC7E,MAAI,aAAa;AACjB,MAAI,WAAW;AACf,MAAI,WAAW;AACf,QAAM,eAAe,CAAC;AACtB,QAAM,iBAAiB,oBAAI,IAAI;AAG/B,aAAW,cAAc,eAAe;AACtC,UAAM,eAAe,WAAW,IAAI;AACpC,UAAM,eAAe,WAAW,IAAI;AAGpC,QAAI,cAAc;AAClB,QAAI,kBAAkB;AACtB,QAAI,kBAAkB;AAEtB,aAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAI,eAAe,IAAI,CAAC,EAAG;AAE3B,YAAM,WAAW,YAAY,CAAC;AAC9B,YAAM,WAAW,KAAK;AAAA,QACpB,KAAK,IAAI,eAAe,SAAS,GAAG,CAAC,IACrC,KAAK,IAAI,eAAe,SAAS,GAAG,CAAC;AAAA,MACvC;AAEA,UAAI,WAAW,aAAa;AAC1B,sBAAc;AACd,0BAAkB;AAClB,0BAAkB;AAAA,MACpB;AAAA,IACF;AAGA,QAAI,eAAe,aAAa,oBAAoB,IAAI;AACtD;AACA,iBAAW,KAAK,IAAI,UAAU,WAAW;AACzC,kBAAY;AACZ,qBAAe,IAAI,eAAe;AAClC,mBAAa,KAAK;AAAA,QAChB;AAAA,QACA,UAAU;AAAA,QACV,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,WAAW,aAAa,IAAK,WAAW,aAAc;AAC5D,SAAO,EAAE,YAAY,UAAU,UAAU,aAAa;AACxD;AAOA,SAAS,uBAAuB,eAAe,aAAa,UAAU,CAAC,GAAG;AACxE,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,WAAW,QAAQ,YAAY;AAErC,MAAI,CAAC,MAAM,QAAQ,aAAa,KAAK,CAAC,MAAM,QAAQ,WAAW,EAAG,QAAO;AACzE,MAAI,cAAc,WAAW,KAAK,YAAY,WAAW,EAAG,QAAO;AAEnE,QAAM,cAAc,iCAAiC,eAAe,KAAK,IAAI,UAAU,cAAc,MAAM,CAAC;AAC5G,QAAM,YAAY,iCAAiC,aAAa,KAAK,IAAI,UAAU,YAAY,MAAM,CAAC;AAEtG,QAAM,OAAO,oBAAI,IAAI;AACrB,aAAW,KAAK,aAAa;AAC3B,eAAW,KAAK,WAAW;AACzB,YAAMC,MAAK,EAAE,IAAI,EAAE;AACnB,YAAMC,MAAK,EAAE,IAAI,EAAE;AACnB,YAAM,KAAK,KAAK,MAAMD,MAAK,OAAO;AAClC,YAAM,KAAK,KAAK,MAAMC,MAAK,OAAO;AAClC,YAAM,MAAM,GAAG,EAAE,IAAI,EAAE;AACvB,YAAM,MAAM,KAAK,IAAI,GAAG,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,EAAE;AAC5D,UAAI,SAAS;AACb,UAAI,SAASD;AACb,UAAI,SAASC;AACb,WAAK,IAAI,KAAK,GAAG;AAAA,IACnB;AAAA,EACF;AAEA,MAAI,UAAU;AACd,MAAI,OAAO;AACX,aAAW,CAAC,GAAG,CAAC,KAAK,KAAK,QAAQ,GAAG;AACnC,QAAI,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO;AACjC,aAAO;AACP,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ,KAAK,QAAQ,SAAU,QAAO;AAC3C,QAAM,KAAK,KAAK,QAAQ,KAAK;AAC7B,QAAM,KAAK,KAAK,QAAQ,KAAK;AAC7B,SAAO,EAAE,IAAI,IAAI,OAAO,KAAK,OAAO,SAAS,KAAK,QAAQ;AAC5D;AAoCA,SAAS,aAAa;AAGpB,MAAI,aAAa;AACjB,MAAI,kBAAkB;AAGtB,QAAM,WAAW,OAAO,KAAK,WAAS;AACpC,UAAMC,YAAW,MAAM;AACvB,UAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,UAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,UAAM,MAAM,cAAc,UAAU,CAAC;AACrC,UAAM,UAAU,oBAAoBA,SAAQ;AAC5C,WAAO,YAAY,SACZ,QAAQ,SACR,aAAa,SAAS,UAAU,KAChC,aAAa,YAAY,EAAE,SAAS,SAAS;AAAA,EACtD,CAAC;AAED,QAAM,WAAW,OAAO,KAAK,WAAS;AACpC,UAAMA,YAAW,MAAM;AACvB,UAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,UAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,UAAM,MAAM,cAAc,UAAU,CAAC;AACrC,UAAM,UAAU,oBAAoBA,SAAQ;AAC5C,WAAO,YAAY,SACZ,QAAQ,SACR,aAAa,SAAS,MAAM,KAC5B,aAAa,SAAS,oBAAoB;AAAA,EACnD,CAAC;AAGD,MAAI,YAAY,SAAS,UAAU,YAAY,SAAS,QAAQ;AAC9D,UAAM,YAAY,SAAS;AAC3B,UAAM,YAAY,SAAS;AAG3B,UAAM,WAAW,UAAU,OAAO,UAAU;AAC5C,UAAM,YAAY,UAAU,OAAO,UAAU;AAC7C,UAAM,UAAU,KAAK,IAAI,UAAU,SAAS;AAE5C,UAAM,WAAW,UAAU,OAAO,UAAU;AAC5C,UAAM,YAAY,UAAU,OAAO,UAAU;AAC7C,UAAM,UAAU,KAAK,IAAI,UAAU,SAAS;AAE5C,UAAM,QAAQ,UAAU,IAAK,UAAU,UAAW;AAClD,YAAQ,IAAI,qDAAkB,QAAQ,QAAQ,CAAC,CAAC,4BAAa,QAAQ,QAAQ,CAAC,CAAC,EAAE;AAEjF,QAAI,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AAEvC,mBAAa,EAAE,GAAG,UAAU;AAC5B,cAAQ,KAAK,uFAAgC,MAAM,QAAQ,CAAC,CAAC,8IAAqC;AAClG,cAAQ,IAAI,0CAAsB,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,EAAE;AAAA,IACxK,OAAO;AAEL,mBAAa;AAAA,QACX,MAAM,KAAK,IAAI,UAAU,MAAM,UAAU,IAAI;AAAA,QAC7C,MAAM,KAAK,IAAI,UAAU,MAAM,UAAU,IAAI;AAAA,QAC7C,MAAM,KAAK,IAAI,UAAU,MAAM,UAAU,IAAI;AAAA,QAC7C,MAAM,KAAK,IAAI,UAAU,MAAM,UAAU,IAAI;AAAA,MAC/C;AACA,cAAQ,IAAI,iHAA4B;AACxC,cAAQ,IAAI,0CAAsB,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,EAAE;AACtK,cAAQ,IAAI,0CAAsB,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,UAAU,UAAU,KAAK,QAAQ,CAAC,CAAC,EAAE;AACtK,cAAQ,IAAI,mDAAqB,WAAW,KAAK,QAAQ,CAAC,CAAC,UAAU,WAAW,KAAK,QAAQ,CAAC,CAAC,UAAU,WAAW,KAAK,QAAQ,CAAC,CAAC,UAAU,WAAW,KAAK,QAAQ,CAAC,CAAC,EAAE;AAAA,IAC3K;AAAA,EACF,WAAW,YAAY,SAAS,QAAQ;AAEtC,iBAAa,EAAE,GAAG,SAAS,OAAO;AAClC,YAAQ,IAAI,qDAAkB,SAAS,IAAI,wCAAe;AAAA,EAC5D,WAAW,YAAY,SAAS,QAAQ;AAEtC,iBAAa,EAAE,GAAG,SAAS,OAAO;AAClC,YAAQ,IAAI,qDAAkB,SAAS,IAAI,wCAAe;AAAA,EAC5D,OAAO;AAEL,UAAM,WAAW,OAAO,KAAK,WAAS;AACpC,YAAMA,YAAW,MAAM;AACvB,YAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,YAAM,gBAAgB,MAAM,aAAa,MAAM,GAAG,EAAE,IAAI,EAAE,YAAY;AACtE,YAAM,MAAM,cAAc,UAAU,CAAC;AACrC,YAAM,UAAU,oBAAoBA,SAAQ;AAC5C,aAAO,YAAY,SACZ,QAAQ,SACR,aAAa,SAAS,aAAa,KAClC,aAAa,YAAY,EAAE,SAAS,QAAQ,KAAK,aAAa,YAAY,EAAE,SAAS,KAAK,KAC1F,aAAa,YAAY,EAAE,SAAS,YAAY,KAAK,aAAa,YAAY,EAAE,SAAS,KAAK;AAAA,IACxG,CAAC;AAED,QAAI,YAAY,SAAS,QAAQ;AAC/B,mBAAa,EAAE,GAAG,SAAS,OAAO;AAClC,cAAQ,IAAI,qDAAkB,SAAS,IAAI,wCAAe;AAAA,IAC5D,OAAO;AAEL,YAAM,WAAW,OAAO,KAAK,WAAS;AACpC,cAAMA,YAAW,MAAM;AACvB,cAAM,eAAeA,UAAS,MAAM,OAAO,EAAE,IAAI;AACjD,eAAO,aAAa,YAAY,EAAE,WAAW,KAAK;AAAA,MACpD,CAAC;AAED,UAAI,YAAY,SAAS,QAAQ;AAC/B,qBAAa,EAAE,GAAG,SAAS,OAAO;AAClC,gBAAQ,IAAI,uEAAqB,SAAS,IAAI,wCAAe;AAAA,MAC/D,OAAO;AAEL,YAAI,OAAO,UAAU,OAAO,WAAW,OAAO,UAAU,OAAO;AAC/D,eAAO,QAAQ,WAAS;AACtB,cAAI,MAAM,QAAQ;AAChB,mBAAO,KAAK,IAAI,MAAM,MAAM,OAAO,IAAI;AACvC,mBAAO,KAAK,IAAI,MAAM,MAAM,OAAO,IAAI;AACvC,mBAAO,KAAK,IAAI,MAAM,MAAM,OAAO,IAAI;AACvC,mBAAO,KAAK,IAAI,MAAM,MAAM,OAAO,IAAI;AAAA,UACzC;AAAA,QACF,CAAC;AACD,qBAAa,EAAE,MAAM,MAAM,MAAM,KAAK;AACtC,0BAAkB;AAClB,gBAAQ,IAAI,0KAA6C;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,WAAY;AAEjB,QAAM,QAAQ,WAAW,OAAO,WAAW;AAC3C,QAAM,SAAS,WAAW,OAAO,WAAW;AAE5C,MAAI,SAAS,KAAK,UAAU,GAAG;AAC7B,YAAQ,KAAK,8DAAsB,KAAK,YAAY,MAAM,EAAE;AAC5D;AAAA,EACF;AAIA,QAAM,eAAe,kBAAkB,MAAO;AAC9C,QAAM,UAAU,KAAK,IAAI,OAAO,MAAM,IAAI;AAC1C,QAAM,cAAc,QAAQ,UAAU;AACtC,QAAM,eAAe,SAAS,UAAU;AAExC,QAAM,MAAM,WAAW,OAAO,WAAW,QAAQ;AACjD,QAAM,MAAM,WAAW,OAAO,WAAW,QAAQ;AAEjD,QAAM,SAAS,OAAO,QAAQ,OAAO;AACrC,QAAM,gBAAgB,cAAc;AAIpC,MAAI,gBAAgB,QAAQ;AAE1B,YAAQ,IAAM,cAAc;AAAA,EAC9B,OAAO;AAEL,YAAS,IAAM,eAAgB,SAAS;AAAA,EAC1C;AAEA,WAAS,CAAC,MAAM,QAAQ;AACxB,WAAS,CAAC,KAAK;AAEf,UAAQ,IAAI,6CAAe,WAAW,KAAK,QAAQ,CAAC,CAAC,KAAK,WAAW,KAAK,QAAQ,CAAC,CAAC,QAAQ,WAAW,KAAK,QAAQ,CAAC,CAAC,KAAK,WAAW,KAAK,QAAQ,CAAC,CAAC,GAAG;AACxJ,UAAQ,IAAI,4CAAc,MAAM,QAAQ,CAAC,CAAC,oBAAU,OAAO,QAAQ,CAAC,CAAC,KAAK,OAAO,QAAQ,CAAC,CAAC,GAAG;AAChG;AAIA,eAAe,6BAA6B,OAAO;AACjD,MAAI,MAAM,WAAW,EAAG;AAExB,QAAM,OAAO,MAAM,CAAC;AACpB,MAAI,CAAC,KAAM;AAIX,QAAM,eAAe,IAAI,aAAa;AACtC,eAAa,MAAM,IAAI,IAAI;AAC3B,YAAU,QAAQ,aAAa;AAG/B,QAAM,QAAQ,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC;AACnD,YAAU,cAAc,KAAK;AAC/B;AAGA,OAAO,iBAAiB,QAAQ,MAAM;AAEpC,mBAAiB,8BAA8B,UAAU;AAGzD,QAAM,UAAU;AAChB,QAAM,eAAe,CAAC,SAAS,GAAG,OAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,CAAC;AACpE,QAAM,eAAe,CAAC,aAAa;AACjC,UAAM,aAAa,aAAa,QAAQ;AACxC,UAAM,KAAK,WAAW;AACtB,QAAI,GAAI,QAAO,GAAG,UAAU,OAAO,mBAAmB,EAAE,CAAC;AACzD,UAAM,MAAM,gBAAgB;AAC5B,QAAI,IAAK,QAAO,GAAG,UAAU,QAAQ,mBAAmB,GAAG,CAAC;AAC5D,WAAO;AAAA,EACT;AAqBA,QAAM,YAAY,SAAS,cAAc,cAAc;AACvD,MAAI,WAAW;AACb,cAAU,iBAAiB,SAAS,MAAM;AACxC,aAAO,SAAS,OAAO,aAAa,SAAS;AAAA,IAC/C,CAAC;AAAA,EACH;AAGA,QAAM,oBAAoB,SAAS,cAAc,sBAAsB;AACvE,MAAI,mBAAmB;AACrB,sBAAkB,iBAAiB,SAAS,MAAM;AAChD,aAAO,SAAS,OAAO,aAAa,aAAa;AAAA,IACnD,CAAC;AAAA,EACH;AACF,CAAC;","names":["scale","earcut","gl","fileName","parseSvgPath","scale","earcut","centerIdx","layers","getLayerType","dx","dy","fileName"]}