/* global buildPiecePath */ class PieceRenderer { /** * Render all puzzle pieces as Phaser textures via off-screen canvas clipping. * In Phaser 3.9, TextureManager emits 'addtexture' with the key as argument * (not 'addtexture-KEY' per-texture events that arrived in later versions). * * @param {Phaser.Scene} scene * @param {HTMLImageElement} sourceImage * @param {PieceData[]} pieceDataArray * @param {number} cols * @param {number} rows * @param {number} imageW * @param {number} imageH * @param {function(number, number)} onProgress * @returns {Promise<{ pieceW, pieceH, tabSize, canvasW, canvasH }>} */ static renderAll(scene, sourceImage, pieceDataArray, cols, rows, imageW, imageH, onProgress) { const pieceW = imageW / cols; const pieceH = imageH / rows; const tabSize = Math.max(pieceW, pieceH) * 0.32; const canvasW = Math.ceil(pieceW + 2 * tabSize); const canvasH = Math.ceil(pieceH + 2 * tabSize); const total = pieceDataArray.length; const dims = { pieceW, pieceH, tabSize, canvasW, canvasH }; // Build a set of expected keys so the listener ignores unrelated textures const pending = new Set(pieceDataArray.map(p => `piece_${p.id}`)); let done = 0; return new Promise(resolve => { // Single listener for all pieces — Phaser 3.9 emits 'addtexture' with key arg const onAdded = (key) => { if (!pending.has(key)) return; pending.delete(key); done++; if (onProgress) onProgress(done, total); if (done === total) { scene.textures.off('addtexture', onAdded); resolve(dims); } }; scene.textures.on('addtexture', onAdded); // Kick off all canvas renders + addBase64 calls pieceDataArray.forEach(piece => { const canvas = document.createElement('canvas'); canvas.width = canvasW; canvas.height = canvasH; const ctx = canvas.getContext('2d'); // Clip path ctx.save(); buildPiecePath(ctx, piece.gridCol, piece.gridRow, cols, rows, pieceW, pieceH, piece.edges, tabSize); ctx.clip(); // Draw the source region that corresponds to the full canvas extent, // including the tab zones that overlap neighbouring cells. // srcX/srcY may be slightly negative for pieces on the top/left border; // the canvas spec clips out-of-bounds source regions automatically. ctx.drawImage( sourceImage, piece.gridCol * pieceW - tabSize, piece.gridRow * pieceH - tabSize, canvasW, canvasH, 0, 0, canvasW, canvasH ); ctx.restore(); // Subtle edge shading ctx.save(); buildPiecePath(ctx, piece.gridCol, piece.gridRow, cols, rows, pieceW, pieceH, piece.edges, tabSize); ctx.clip(); ctx.strokeStyle = 'rgba(0,0,0,0.35)'; ctx.lineWidth = 3; ctx.stroke(); ctx.restore(); const key = `piece_${piece.id}`; if (scene.textures.exists(key)) scene.textures.remove(key); scene.textures.addBase64(key, canvas.toDataURL('image/png')); }); }); } }