From 35dcf76689e4f2a03b2d2cd7c3edc0da0867b678 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Sun, 10 Aug 2025 15:33:51 -0500 Subject: [PATCH] web preview display improvements --- templates/index_v2.html | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/templates/index_v2.html b/templates/index_v2.html index d242a45b..5cc70ed1 100644 --- a/templates/index_v2.html +++ b/templates/index_v2.html @@ -141,6 +141,16 @@ pointer-events: none; } + .led-canvas { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + background: #000; + } + .display-image { max-width: 100%; max-height: 100%; @@ -731,6 +741,7 @@
@@ -762,6 +773,15 @@ Show pixel grid + +
@@ -1778,6 +1798,7 @@ gridCanvas.width = data.width * scale; gridCanvas.height = data.height * scale; drawGrid(gridCanvas, data.width, data.height, scale); + renderLedDots(); }) .catch(() => {}); }); @@ -1801,6 +1822,20 @@ // default hidden gridCanvas.style.display = 'none'; } + + // LED dot mode controls + const toggleLedDots = document.getElementById('toggleLedDots'); + const dotFillRange = document.getElementById('dotFillRange'); + const dotFillValue = document.getElementById('dotFillValue'); + if (dotFillRange && dotFillValue) { + dotFillRange.addEventListener('input', () => { + dotFillValue.textContent = `${dotFillRange.value}%`; + renderLedDots(); + }); + } + if (toggleLedDots) { + toggleLedDots.addEventListener('change', renderLedDots); + } // Update stats every 30 seconds setInterval(updateSystemStats, 30000); @@ -1914,6 +1949,7 @@ const stage = document.getElementById('previewStage'); const img = document.getElementById('displayImage'); const canvas = document.getElementById('gridOverlay'); + const ledCanvas = document.getElementById('ledCanvas'); const placeholder = document.getElementById('displayPlaceholder'); if (data.image) { @@ -1937,9 +1973,12 @@ const height = (data.height || 32) * scale; img.style.width = width + 'px'; img.style.height = height + 'px'; + ledCanvas.width = width; + ledCanvas.height = height; canvas.width = width; canvas.height = height; drawGrid(canvas, data.width || 128, data.height || 32, scale); + renderLedDots(); } else { stage.style.display = 'none'; placeholder.style.display = 'block'; @@ -1950,6 +1989,54 @@ } } + function renderLedDots(){ + const ledCanvas = document.getElementById('ledCanvas'); + const img = document.getElementById('displayImage'); + const toggle = document.getElementById('toggleLedDots'); + if (!ledCanvas || !img || !toggle) return; + const show = toggle.checked; + ledCanvas.style.display = show ? 'block' : 'none'; + if (!show) return; + + const scale = parseInt(document.getElementById('scaleRange').value || '8'); + const fillPct = parseInt(document.getElementById('dotFillRange').value || '75'); + const dotRadius = Math.max(1, Math.floor((scale * fillPct) / 200)); // radius in px + const ctx = ledCanvas.getContext('2d'); + ctx.clearRect(0, 0, ledCanvas.width, ledCanvas.height); + + // Draw black background (gaps between LEDs) + ctx.fillStyle = '#000'; + ctx.fillRect(0, 0, ledCanvas.width, ledCanvas.height); + + // Create an offscreen canvas to sample pixel colors + const off = document.createElement('canvas'); + const logicalWidth = Math.floor(ledCanvas.width / scale); + const logicalHeight = Math.floor(ledCanvas.height / scale); + off.width = logicalWidth; + off.height = logicalHeight; + const offCtx = off.getContext('2d'); + // Draw the current image scaled down to logical LEDs to sample colors + try { + offCtx.drawImage(img, 0, 0, logicalWidth, logicalHeight); + } catch (_) { /* draw failures ignored */ } + + // Draw circular dots for each LED pixel + for (let y = 0; y < logicalHeight; y++) { + for (let x = 0; x < logicalWidth; x++) { + const pixel = offCtx.getImageData(x, y, 1, 1).data; + const r = pixel[0], g = pixel[1], b = pixel[2], a = pixel[3]; + // Skip fully black to reduce overdraw + if (a === 0 || (r|g|b) === 0) continue; + ctx.fillStyle = `rgb(${r},${g},${b})`; + const cx = Math.floor(x * scale + scale / 2); + const cy = Math.floor(y * scale + scale / 2); + ctx.beginPath(); + ctx.arc(cx, cy, dotRadius, 0, Math.PI * 2); + ctx.fill(); + } + } + } + // Tab functionality function showTab(tabName) { // Hide all tab contents