/** RX quality vs distance chart for TX/RX track compare. */ (function (global) { 'use strict'; function drawQualityDistChart(canvas, samples, qualityColor, highlightDist) { if (!canvas) return; const ctx = canvas.getContext('2d'); const w = canvas.clientWidth || 280; const h = canvas.clientHeight || 140; if (canvas.width !== w) canvas.width = w; if (canvas.height !== h) canvas.height = h; ctx.fillStyle = '#0a0a14'; ctx.fillRect(0, 0, w, h); if (!samples?.length) { ctx.fillStyle = '#888'; ctx.font = '11px system-ui'; ctx.fillText('нет данных качества', 12, h / 2); return; } const dists = samples.map(s => s.distM); const minD = Math.min(...dists); const maxD = Math.max(...dists); const span = Math.max(maxD - minD, 1); const binCount = Math.min(20, Math.max(5, Math.ceil(span / 10))); const binW = span / binCount; const bins = Array.from({ length: binCount }, (_, i) => ({ min: minD + i * binW, max: minD + (i + 1) * binW, qualities: [], })); for (const s of samples) { let idx = Math.floor((s.distM - minD) / binW); if (idx >= binCount) idx = binCount - 1; if (idx < 0) idx = 0; bins[idx].qualities.push(s.quality); } const margin = { l: 36, r: 8, t: 16, b: 22 }; const plotW = w - margin.l - margin.r; const plotH = h - margin.t - margin.b; ctx.strokeStyle = '#333'; ctx.beginPath(); ctx.moveTo(margin.l, margin.t); ctx.lineTo(margin.l, margin.t + plotH); ctx.lineTo(margin.l + plotW, margin.t + plotH); ctx.stroke(); ctx.fillStyle = '#888'; ctx.font = '9px system-ui'; ctx.fillText('0%', 2, margin.t + plotH); ctx.fillText('100%', 2, margin.t + 8); ctx.fillText(`${Math.round(minD)}m`, margin.l, h - 2); ctx.fillText(`${Math.round(maxD)}m`, margin.l + plotW - 24, h - 2); ctx.fillStyle = '#ccc'; ctx.font = '10px system-ui'; ctx.fillText('RX Quality vs расстояние', margin.l, margin.t - 4); const barW = plotW / binCount * 0.75; bins.forEach((b, i) => { if (!b.qualities.length) return; const avg = b.qualities.reduce((a, v) => a + v, 0) / b.qualities.length; const cx = margin.l + (i + 0.5) / binCount * plotW; const barH = (avg / 100) * plotH; const x = cx - barW / 2; const y = margin.t + plotH - barH; const col = qualityColor ? qualityColor(avg) : '#888'; const highlight = highlightDist != null && highlightDist >= b.min && highlightDist < b.max; ctx.fillStyle = col; ctx.globalAlpha = highlight ? 1 : 0.75; ctx.fillRect(x, y, barW, barH); ctx.globalAlpha = 1; if (highlight) { ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5; ctx.strokeRect(x, y, barW, barH); } }); ctx.fillStyle = 'rgba(255,255,255,0.25)'; samples.forEach(s => { const x = margin.l + ((s.distM - minD) / span) * plotW; const y = margin.t + plotH - (s.quality / 100) * plotH; ctx.beginPath(); ctx.arc(x, y, 1.5, 0, Math.PI * 2); ctx.fill(); }); } global.QualityViz = { drawQualityDistChart, }; })(typeof window !== 'undefined' ? window : globalThis);