resonanzband_analysis/data_visualization/js/visualization.js

136 lines
5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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<Object>} 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 = `
<div class="summary__metric"><span class="summary__label">Durchschnitt Bandcenter:</span> <span class="summary__value">${summary.avgBandCenter}</span></div>
<div class="summary__metric"><span class="summary__label">Durchschnitt Bandbreite (P10P90):</span> <span class="summary__value">${summary.avgWidth}</span></div>
<div class="summary__metric"><span class="summary__label">Maximaler Ausreißer:</span> <span class="summary__value">${summary.maxOutlier}</span></div>
<div class="summary__metric"><span class="summary__label">Mittlerer Overhead (ms):</span> <span class="summary__value">${summary.avgOverhead}</span></div>
`;
}
/**
* Zeichnet Diagramme und Statistiken im charts-container basierend auf Analyseergebnissen.
* @param {Array<Object>} 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);
}