(gridImageData, paletteMeta)
| 898 | } |
| 899 | |
| 900 | function renderBeadPattern(gridImageData, paletteMeta) { |
| 901 | if (!gridImageData || !paletteMeta || !paletteMeta.codeMap) return null; |
| 902 | |
| 903 | const { width: gridW, height: gridH } = gridImageData; |
| 904 | const data = gridImageData.data; |
| 905 | const cellSize = MIN_CELL_SIZE; |
| 906 | |
| 907 | const canvasW = gridW * cellSize; |
| 908 | const canvasH = gridH * cellSize + HEADER_HEIGHT; |
| 909 | |
| 910 | const patternCanvas = document.createElement('canvas'); |
| 911 | patternCanvas.width = canvasW; |
| 912 | patternCanvas.height = canvasH; |
| 913 | const pCtx = patternCanvas.getContext('2d'); |
| 914 | |
| 915 | // Draw brand header |
| 916 | pCtx.fillStyle = '#f5f5f5'; |
| 917 | pCtx.fillRect(0, 0, canvasW, HEADER_HEIGHT); |
| 918 | pCtx.fillStyle = '#333333'; |
| 919 | pCtx.font = 'bold 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'; |
| 920 | pCtx.textAlign = 'left'; |
| 921 | pCtx.textBaseline = 'middle'; |
| 922 | pCtx.fillText(t('bead.patternTitle', { brand: paletteMeta.brand || 'Bead' }), 12, HEADER_HEIGHT / 2); |
| 923 | |
| 924 | // Build fast lookup: color key → { r, g, b, fullCode } |
| 925 | const colorEntries = []; |
| 926 | for (const [key, fullCode] of Object.entries(paletteMeta.codeMap)) { |
| 927 | const [r, g, b] = key.split(',').map(Number); |
| 928 | colorEntries.push({ r, g, b, key, fullCode }); |
| 929 | } |
| 930 | |
| 931 | // Nearest-color lookup with cache |
| 932 | const codeCache = new Map(); |
| 933 | function findCode(r, g, b) { |
| 934 | const key = `${r},${g},${b}`; |
| 935 | if (codeCache.has(key)) return codeCache.get(key); |
| 936 | |
| 937 | let minDist = Infinity; |
| 938 | let bestCode = null; |
| 939 | for (const entry of colorEntries) { |
| 940 | const dr = r - entry.r; |
| 941 | const dg = g - entry.g; |
| 942 | const db = b - entry.b; |
| 943 | const dist = dr * dr + dg * dg + db * db; |
| 944 | if (dist < minDist) { |
| 945 | minDist = dist; |
| 946 | bestCode = entry.fullCode; |
| 947 | } |
| 948 | } |
| 949 | codeCache.set(key, bestCode); |
| 950 | return bestCode; |
| 951 | } |
| 952 | |
| 953 | // Compute optimal font size: each single digit width must not exceed 12px |
| 954 | const MAX_DIGIT_WIDTH = 12; |
| 955 | let fontSize = 18; // enlarged ~80% from previous 10px baseline |
| 956 | const fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif"; |
| 957 | pCtx.font = `bold ${fontSize}px ${fontFamily}`; |
no test coverage detected