Add visualization_tool/js/charts.js

This commit is contained in:
Mika 2026-02-24 13:33:04 +00:00
parent 7dd2ee785a
commit 8dddf4fcf4

View file

@ -0,0 +1,123 @@
/**
* 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. Δt0/<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');
}