/** * js/charts.js * Visualisierung der Laufdaten in Matrix- und Zeitdiagrammen. * Enthält Funktionen zur DOM-Aktualisierung von #delta-matrix und #timeline-chart. * @module charts */ /** * Rendert eine 2×2-Matrix aus den gegebenen aggregierten Daten. * Die Matrix zeigt pinned/unpinned (x) vs. Δt≥0/<0 (y). * @param {Object} data - Aggregierte Daten in der Form: * { * pinned_pos: number, * pinned_neg: number, * unpinned_pos: number, * unpinned_neg: number * } */ export function renderDeltaMatrix(data) { const container = document.getElementById('delta-matrix'); if (!container) return; container.innerHTML = ''; const matrixData = [ { label: 'Pinned Δt≥0', value: data?.pinned_pos ?? 0 }, { label: 'Pinned Δt<0', value: data?.pinned_neg ?? 0 }, { label: 'Unpinned Δt≥0', value: data?.unpinned_pos ?? 0 }, { label: 'Unpinned Δt<0', value: data?.unpinned_neg ?? 0 } ]; const matrixGrid = document.createElement('div'); matrixGrid.className = 'delta-matrix__grid'; matrixGrid.style.display = 'grid'; matrixGrid.style.gridTemplateColumns = 'repeat(2, 1fr)'; matrixGrid.style.gap = '1rem'; matrixData.forEach(cell => { const cellDiv = document.createElement('div'); cellDiv.className = 'delta-matrix__cell'; cellDiv.setAttribute('role', 'region'); cellDiv.setAttribute('aria-label', cell.label); const label = document.createElement('div'); label.className = 'delta-matrix__label'; label.textContent = cell.label; const value = document.createElement('div'); value.className = 'delta-matrix__value'; value.textContent = cell.value.toFixed(2); cellDiv.appendChild(label); cellDiv.appendChild(value); matrixGrid.appendChild(cellDiv); }); container.appendChild(matrixGrid); } /** * Rendert den zeitlichen Verlauf der Δt-Werte als Liniendiagramm. * Nutzt Canvas API (ohne externes Chart-Framework). * @param {Array<{timestamp: string|number, delta_t: number}>} timeData - Zeitreihen-Daten. */ export function renderTimelineChart(timeData) { const container = document.getElementById('timeline-chart'); if (!container) return; container.innerHTML = ''; const canvas = document.createElement('canvas'); canvas.width = container.clientWidth || 600; canvas.height = 300; container.appendChild(canvas); const ctx = canvas.getContext('2d'); if (!ctx || !Array.isArray(timeData) || timeData.length === 0) { ctx.fillStyle = '#666'; ctx.fillText('Keine Daten verfügbar', 20, 40); return; } const maxVal = Math.max(...timeData.map(d => d.delta_t)); const minVal = Math.min(...timeData.map(d => d.delta_t)); const normalize = v => (v - minVal) / (maxVal - minVal || 1); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.beginPath(); ctx.lineWidth = 2; ctx.strokeStyle = '#0077cc'; timeData.forEach((point, i) => { const x = (i / (timeData.length - 1)) * (canvas.width - 40) + 20; const y = canvas.height - (normalize(point.delta_t) * (canvas.height - 40) + 20); if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); }); ctx.stroke(); // Achsen zeichnen ctx.strokeStyle = '#333'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(20, 10); ctx.lineTo(20, canvas.height - 20); ctx.lineTo(canvas.width - 20, canvas.height - 20); ctx.stroke(); // Beschriftungen ctx.fillStyle = '#222'; ctx.font = '12px sans-serif'; ctx.fillText('Zeit', canvas.width / 2 - 20, canvas.height - 5); ctx.save(); ctx.rotate(-Math.PI / 2); ctx.fillText('Δt', -canvas.height / 2 - 10, 10); ctx.restore(); // Accessibility-Hinweis canvas.setAttribute('role', 'img'); canvas.setAttribute('aria-label', 'Liniendiagramm der Δt-Zeitverteilung'); }