This commit is contained in:
2026-06-22 10:45:02 +03:00
parent e17e36a4c9
commit 73f8fecabe
+90 -43
View File
@@ -2,18 +2,16 @@
(function (global) {
'use strict';
const METER_STEP = 10;
const QUALITY_BUCKET = 10;
function niceMeterStep(span) {
if (span <= 50) return 10;
if (span <= 150) return 25;
if (span <= 400) return 50;
return 100;
function niceMeterStep() {
return METER_STEP;
}
function niceMeterTicks(minD, maxD) {
const span = Math.max(maxD - minD, 1);
const step = niceMeterStep(span);
const step = METER_STEP;
const ticks = [];
const start = Math.ceil(minD / step) * step;
for (let d = start; d <= maxD + step * 0.01; d += step) {
@@ -35,6 +33,20 @@
return Math.min(100, Math.max(0, q));
}
function colorWithAlpha(hex, alpha) {
if (!hex || typeof hex !== 'string') {
return `rgba(136, 136, 136, ${alpha})`;
}
const h = hex.replace('#', '');
if (h.length !== 6) {
return `rgba(136, 136, 136, ${alpha})`;
}
const r = parseInt(h.slice(0, 2), 16);
const g = parseInt(h.slice(2, 4), 16);
const b = parseInt(h.slice(4, 6), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
function drawMeterAxis(ctx, margin, plotW, plotH, h, minD, span) {
const ticks = niceMeterTicks(minD, minD + span);
const minLabelGap = 28;
@@ -73,6 +85,45 @@
return hist;
}
/** Solid horizontal quality distribution inside one distance bin (0% left → 100% right). */
function drawBinQualityStrip(ctx, x0, barW, plotTop, plotH, hist, maxInBin, qualityColor, highlight) {
const cellW = barW / (100 / QUALITY_BUCKET);
const buckets = Array.from(hist.keys()).sort((a, b) => a - b);
for (const bq of buckets) {
const n = hist.get(bq);
if (!n) continue;
const frac = n / maxInBin;
const cellX = x0 + (bq / 100) * barW;
const fillH = Math.max(1, frac * plotH);
const y = plotTop + plotH - fillH;
const base = qualityColor ? qualityColor(bq) : '#888888';
const alpha = highlight ? 0.35 + 0.65 * frac : 0.18 + 0.72 * frac;
ctx.fillStyle = colorWithAlpha(base, alpha);
ctx.fillRect(cellX, y, Math.max(1, cellW + 0.5), fillH);
}
// Smooth overlap between neighbours for a continuous look
if (buckets.length >= 2) {
for (let i = 0; i < buckets.length - 1; i++) {
const q0 = buckets[i];
const q1 = buckets[i + 1];
const n0 = hist.get(q0);
const n1 = hist.get(q1);
const h0 = (n0 / maxInBin) * plotH;
const h1 = (n1 / maxInBin) * plotH;
const x0p = x0 + (q0 / 100) * barW;
const x1p = x0 + (q1 / 100) * barW;
const midX = (x0p + x1p + cellW) / 2;
const midH = Math.max(h0, h1) * 0.55;
const midQ = (q0 + q1) / 2;
const base = qualityColor ? qualityColor(midQ) : '#888888';
ctx.fillStyle = colorWithAlpha(base, highlight ? 0.22 : 0.12);
ctx.fillRect(midX - cellW * 0.4, plotTop + plotH - midH, cellW * 0.8, midH);
}
}
}
function drawQualityDistChart(canvas, samples, qualityColor, highlightDist) {
if (!canvas) return;
const ctx = canvas.getContext('2d');
@@ -94,8 +145,7 @@
const minD = Math.min(...dists);
const maxD = Math.max(...dists);
const span = Math.max(maxD - minD, 1);
const meterStep = niceMeterStep(span);
const binCount = Math.min(60, Math.max(1, Math.ceil(span / meterStep)));
const binCount = Math.max(1, Math.ceil(span / METER_STEP));
const binWDist = span / binCount;
const bins = Array.from({ length: binCount }, (_, i) => ({
min: minD + i * binWDist,
@@ -109,36 +159,52 @@
bins[idx].qualities.push(s.quality);
}
const margin = { l: 36, r: 8, t: 16, b: 30 };
const margin = { l: 36, r: 8, t: 16, b: 34 };
const plotW = w - margin.l - margin.r;
const plotH = h - margin.t - margin.b;
const barW = plotW / binCount;
const markerH = Math.max(3, Math.min(6, barW * 0.45));
const plotTop = margin.t;
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.moveTo(margin.l, plotTop);
ctx.lineTo(margin.l, plotTop + plotH);
ctx.lineTo(margin.l + plotW, plotTop + 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('0', 2, plotTop + plotH);
ctx.fillText('max', 2, plotTop + 8);
drawMeterAxis(ctx, margin, plotW, plotH, h, minD, span);
ctx.fillStyle = '#666';
ctx.font = '8px system-ui';
ctx.fillText('RX 0%', margin.l, h - 14);
const rx100 = 'RX 100%';
const rx100w = ctx.measureText(rx100).width;
ctx.fillText(rx100, margin.l + plotW - rx100w, h - 14);
ctx.fillText('плотность ↑', 2, plotTop + plotH / 2 + 12);
bins.forEach((b, i) => {
const x0 = margin.l + i / binCount * plotW;
const cx = x0 + barW / 2;
const highlight = highlightDist != null
&& highlightDist >= b.min && highlightDist < b.max;
const count = b.qualities.length;
if (i > 0) {
ctx.strokeStyle = 'rgba(255,255,255,0.04)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(x0, plotTop);
ctx.lineTo(x0, plotTop + plotH);
ctx.stroke();
}
if (count === 0) {
ctx.fillStyle = 'rgba(255,255,255,0.06)';
ctx.fillRect(x0, margin.t + plotH - 1, barW, 1);
ctx.fillStyle = 'rgba(255,255,255,0.04)';
ctx.fillRect(x0, plotTop + plotH - 1, barW, 1);
return;
}
@@ -149,38 +215,19 @@
});
if (highlight) {
ctx.fillStyle = 'rgba(255,255,255,0.06)';
ctx.fillRect(x0, margin.t, barW, plotH);
ctx.fillStyle = 'rgba(255,255,255,0.05)';
ctx.fillRect(x0, plotTop, barW, plotH);
}
const sortedBuckets = Array.from(hist.keys()).sort((a, b) => a - b);
for (const bq of sortedBuckets) {
const n = hist.get(bq);
const frac = n / maxInBin;
const segW = Math.max(2, frac * (barW - 1));
const y = margin.t + plotH - (bq / 100) * plotH;
const col = qualityColor ? qualityColor(bq) : '#888';
ctx.fillStyle = col;
ctx.globalAlpha = highlight ? 0.95 : (0.45 + 0.55 * frac);
ctx.fillRect(cx - segW / 2, y - markerH / 2, segW, markerH);
ctx.globalAlpha = 1;
}
drawBinQualityStrip(
ctx, x0, barW, plotTop, plotH, hist, maxInBin, qualityColor, highlight);
if (highlight) {
ctx.strokeStyle = 'rgba(255,255,255,0.85)';
ctx.strokeStyle = 'rgba(255,255,255,0.8)';
ctx.lineWidth = 1.5;
ctx.strokeRect(x0 + 0.5, margin.t + 0.5, barW - 1, plotH - 1);
ctx.strokeRect(x0 + 0.5, plotTop + 0.5, barW - 1, plotH - 1);
}
});
ctx.fillStyle = 'rgba(255,255,255,0.18)';
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.2, 0, Math.PI * 2);
ctx.fill();
});
}
global.QualityViz = {