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 @@
![LED Matrix Display]()
+
@@ -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