/** * visualization.js * Verantwortlich für Rendering der Diagramme und Aktualisierung bei neuen Daten. * Part of experiment: resonanzband_analysis (artifact: data_visualization) */ /** * Aktualisiert die aggregierten Kennzahlen im summary-section. * @param {Array} data - Analyseergebnisse mit Feldern { run_id, band_center, band_width_p10_p90, max_outlier, overhead_ms } */ export function updateSummary(data) { if (!Array.isArray(data) || data.length === 0) { const summaryEl = document.getElementById('summary-section'); if (summaryEl) summaryEl.textContent = 'Keine Daten verfügbar.'; return; } const avg = (arr, key) => arr.reduce((sum, d) => sum + (d[key] ?? 0), 0) / arr.length; const maxOutlier = Math.max(...data.map(d => d.max_outlier ?? 0)); const summary = { avgBandCenter: avg(data, 'band_center').toFixed(2), avgWidth: avg(data, 'band_width_p10_p90').toFixed(2), maxOutlier, avgOverhead: avg(data, 'overhead_ms').toFixed(2) }; const summaryEl = document.getElementById('summary-section'); if (!summaryEl) return; summaryEl.innerHTML = `
Durchschnitt Bandcenter: ${summary.avgBandCenter}
Durchschnitt Bandbreite (P10–P90): ${summary.avgWidth}
Maximaler Ausreißer: ${summary.maxOutlier}
Mittlerer Overhead (ms): ${summary.avgOverhead}
`; } /** * Zeichnet Diagramme und Statistiken im charts-container basierend auf Analyseergebnissen. * @param {Array} results - Analyseergebnisse */ export function renderResults(results) { const container = document.getElementById('charts-container'); if (!container) return; container.innerHTML = ''; if (!Array.isArray(results) || results.length === 0) { container.textContent = 'Keine Ergebnisse gefunden.'; updateSummary([]); return; } // Diagramm mit Canvas-Element zeichnen (native Lösung ohne externe Lib) const canvas = document.createElement('canvas'); canvas.width = container.clientWidth; canvas.height = 300; canvas.setAttribute('role', 'img'); canvas.setAttribute('aria-label', 'Diagramm der Resonanzband-Metriken'); container.appendChild(canvas); const ctx = canvas.getContext('2d'); const padding = 40; const width = canvas.width - 2 * padding; const height = canvas.height - 2 * padding; const maxBand = Math.max(...results.map(r => r.band_center + r.band_width_p10_p90)); const minBand = Math.min(...results.map(r => r.band_center - r.band_width_p10_p90)); const scaleY = val => height - ((val - minBand) / (maxBand - minBand)) * height + padding; const scaleX = (i) => padding + (i / (results.length - 1)) * width; // Achsen zeichnen ctx.strokeStyle = '#333'; ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, height + padding); ctx.lineTo(width + padding, height + padding); ctx.stroke(); // Linien für Bandcenter zeichnen ctx.strokeStyle = '#0057b7'; ctx.lineWidth = 2; ctx.beginPath(); results.forEach((r, i) => { const x = scaleX(i); const y = scaleY(r.band_center); if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); }); ctx.stroke(); // Outlier Punkte zeichnen ctx.fillStyle = '#b70000'; results.forEach((r, i) => { const x = scaleX(i); const y = scaleY(r.band_center + r.max_outlier); ctx.beginPath(); ctx.arc(x, y, 3, 0, Math.PI * 2); ctx.fill(); }); // Tooltips: einfache textuelle Version canvas.addEventListener('mousemove', e => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; const idx = Math.round((x - padding) / width * (results.length - 1)); const item = results[idx]; if (!item) return; const tooltip = document.getElementById('chart-tooltip') ?? document.createElement('div'); tooltip.id = 'chart-tooltip'; tooltip.className = 'chart__tooltip'; tooltip.style.position = 'absolute'; tooltip.style.left = `${x + 10}px`; tooltip.style.top = `${y + 10}px`; tooltip.style.background = '#222'; tooltip.style.color = '#fff'; tooltip.style.padding = '4px 8px'; tooltip.style.borderRadius = '4px'; tooltip.style.fontSize = '0.8rem'; tooltip.style.pointerEvents = 'none'; tooltip.textContent = `Run ${item.run_id}: Center=${item.band_center}, Width=${item.band_width_p10_p90}, Outlier=${item.max_outlier}`; document.body.appendChild(tooltip); }); canvas.addEventListener('mouseleave', () => { const tooltip = document.getElementById('chart-tooltip'); if (tooltip) tooltip.remove(); }); // Zusammenfassung aktualisieren updateSummary(results); }