From 57e6754461f27745c00bb83ff72e2f67d69d8506 Mon Sep 17 00:00:00 2001 From: "alexei.dolgolyov" Date: Sun, 8 Feb 2026 05:16:17 +0300 Subject: [PATCH] Add smart tick labels and move direction arrows inside screen - Add intermediate tick labels at nice intervals (5, 10, 25, 50, 100, 250, 500) with max 5 labels per edge and pixel-based overlap prevention - Move direction chevrons from outside edge bars to inside the screen area - Make chevrons filled with green fill and white outline for better contrast Co-Authored-By: Claude Opus 4.6 --- server/src/wled_controller/static/app.js | 70 +++++++++++++++++++----- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/server/src/wled_controller/static/app.js b/server/src/wled_controller/static/app.js index f16d601..9f1a5a2 100644 --- a/server/src/wled_controller/static/app.js +++ b/server/src/wled_controller/static/app.js @@ -1219,12 +1219,13 @@ function renderCalibrationCanvas() { right: ox + cW + 3, // labels right of right edge }; - // Arrow positions (further out than tick labels) + // Arrow positions (inside the screen area, near each edge bar) + const arrowInset = 12; const arrowPos = { - top: oy - 10, - bottom: oy + cH + 10, - left: ox - 10, - right: ox + cW + 10, + top: oy + ch + arrowInset, + bottom: oy + cH - ch - arrowInset, + left: ox + cw + arrowInset, + right: ox + cW - cw - arrowInset, }; // Draw ticks and direction arrows for each segment @@ -1235,17 +1236,55 @@ function renderCalibrationCanvas() { const count = seg.led_count; if (count === 0) return; - // Show only first and last LED index per edge, plus LED 0 if offset > 0 + // Mandatory ticks: first and last LED index per edge, plus LED 0 if offset > 0 const labelsToShow = new Set(); labelsToShow.add(0); if (count > 1) labelsToShow.add(count - 1); - // Add LED 0 tick on the edge where it wraps if (offset > 0 && totalLeds > 0) { const zeroPos = (totalLeds - seg.led_start % totalLeds) % totalLeds; if (zeroPos < count) labelsToShow.add(zeroPos); } + // Add intermediate ticks at "nice" intervals (max 5 labels per edge) + if (count > 2) { + const edgeLen = geo.horizontal ? (geo.x2 - geo.x1) : (geo.y2 - geo.y1); + const maxDigits = String(totalLeds > 0 ? totalLeds - 1 : count - 1).length; + const minSpacing = geo.horizontal ? maxDigits * 7 + 8 : 22; + + const maxIntermediate = Math.max(0, 5 - labelsToShow.size); + const niceSteps = [5, 10, 25, 50, 100, 250, 500]; + let step = niceSteps[niceSteps.length - 1]; + for (const s of niceSteps) { + if (Math.floor(count / s) <= maxIntermediate) { + step = s; + break; + } + } + + // Pixel position helper (0..edgeLen along the edge) + const tickPx = i => { + const f = i / (count - 1); + return (seg.reverse ? (1 - f) : f) * edgeLen; + }; + + // Collect pixel positions of mandatory ticks + const placed = []; + labelsToShow.forEach(i => placed.push(tickPx(i))); + + // Add ticks at LED indices divisible by step + for (let i = 1; i < count - 1; i++) { + const idx = totalLeds > 0 ? (seg.led_start + i) % totalLeds : seg.led_start + i; + if (idx % step === 0) { + const px = tickPx(i); + if (!placed.some(p => Math.abs(px - p) < minSpacing)) { + labelsToShow.add(i); + placed.push(px); + } + } + } + } + // Tick styling const tickLen = 5; ctx.strokeStyle = 'rgba(255, 255, 255, 0.4)'; @@ -1291,8 +1330,8 @@ function renderCalibrationCanvas() { } }); - // Draw direction chevron at midpoint, outside the edge bar - const s = 5; + // Draw direction chevron at midpoint, inside the screen area + const s = 7; let mx, my, angle; if (geo.horizontal) { mx = (geo.x1 + geo.x2) / 2; @@ -1307,14 +1346,17 @@ function renderCalibrationCanvas() { ctx.save(); ctx.translate(mx, my); ctx.rotate(angle); - ctx.strokeStyle = '#4CAF50'; - ctx.lineWidth = 1.5; + ctx.fillStyle = 'rgba(76, 175, 80, 0.85)'; + ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)'; + ctx.lineWidth = 1; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.beginPath(); - ctx.moveTo(-s * 0.4, -s * 0.5); - ctx.lineTo(s * 0.4, 0); - ctx.lineTo(-s * 0.4, s * 0.5); + ctx.moveTo(-s * 0.5, -s * 0.6); + ctx.lineTo(s * 0.5, 0); + ctx.lineTo(-s * 0.5, s * 0.6); + ctx.closePath(); + ctx.fill(); ctx.stroke(); ctx.restore(); });