generated from Grigo/AndroidTemplate
fix
This commit is contained in:
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user