Refactor: Replace all 'var' with 'let' (JS keyword only)

This commit is contained in:
Dieter Schlüter 2026-04-06 19:50:44 +02:00
commit 8d16936f5d

View file

@ -197,7 +197,7 @@
<div class="flex-1"></div> <div class="flex-1"></div>
<div class="text-center"> <div class="text-center">
<h1 class="title-text font-bold tracking-tight" style="color: var(--c-text); text-shadow: 0 0 40px rgba(74,124,255,0.3);"> <h1 class="title-text font-bold tracking-tight" style="color: var(--c-text); text-shadow: 0 0 40px rgba(74,124,255,0.3);">
Sortier-Algorithmen <span style="font-size: 0.45em; font-weight: 400; opacity: 0.5; vertical-align: middle;">v0.2.6</span> Sortier-Algorithmen <span style="font-size: 0.45em; font-weight: 400; opacity: 0.5; vertical-align: middle;">v0.2.7</span>
</h1> </h1>
<p class="text-muted text-sm mt-0.5">Interaktive Visualisierung mit schrittweiser Animation</p> <p class="text-muted text-sm mt-0.5">Interaktive Visualisierung mit schrittweiser Animation</p>
</div> </div>
@ -970,7 +970,7 @@ function buildExplanation(step) {
const idx = step.indices; const idx = step.indices;
const arr = step.array; const arr = step.array;
function fmtVal(i) { function fmtVal(i) {
var v = arr[i]; let v = arr[i];
return Number.isInteger(v) ? String(v) : v.toFixed(1); return Number.isInteger(v) ? String(v) : v.toFixed(1);
} }
switch (step.type) { switch (step.type) {
@ -1178,21 +1178,21 @@ function buildSteps(algoName) {
let heapSize = n; let heapSize = n;
let heapPhase = 'Heap aufbauen'; let heapPhase = 'Heap aufbauen';
function getHeapState(highlight) { function getHeapState(highlight) {
var nodes = []; let nodes = [];
for (var hi = 0; hi < heapSize; hi++) { for (let hi = 0; hi < heapSize; hi++) {
var depth = Math.floor(Math.log2(hi + 1)); let depth = Math.floor(Math.log2(hi + 1));
nodes.push({ val: arr[hi], left: 2 * hi + 1 < heapSize ? 2 * hi + 1 : -1, right: 2 * hi + 2 < heapSize ? 2 * hi + 2 : -1, idx: hi, depth: depth }); nodes.push({ val: arr[hi], left: 2 * hi + 1 < heapSize ? 2 * hi + 1 : -1, right: 2 * hi + 2 < heapSize ? 2 * hi + 2 : -1, idx: hi, depth: depth });
} }
// BFS-Positionen: In-Order ist hier nicht ideal, Level-Order-X-Positionen berechnen // BFS-Positionen: In-Order ist hier nicht ideal, Level-Order-X-Positionen berechnen
var maxD = nodes.length > 0 ? nodes[nodes.length - 1].depth : 0; let maxD = nodes.length > 0 ? nodes[nodes.length - 1].depth : 0;
var drawNodes = []; let drawNodes = [];
for (var hi = 0; hi < nodes.length; hi++) { let totalWidth = Math.pow(2, maxD);
var nd = nodes[hi], d = nd.depth; for (let hi = 0; hi < nodes.length; hi++) {
let nd = nodes[hi], d = nd.depth;
// Position in der Ebene // Position in der Ebene
var posInLevel = hi - (Math.pow(2, d) - 1); let posInLevel = hi - (Math.pow(2, d) - 1);
var levelCount = Math.min(Math.pow(2, d), heapSize - (Math.pow(2, d) - 1)); let levelCount = Math.min(Math.pow(2, d), heapSize - (Math.pow(2, d) - 1));
var totalWidth = Math.pow(2, maxD); let x = (posInLevel + 0.5) * (totalWidth / Math.pow(2, d));
var x = (posInLevel + 0.5) * (totalWidth / Math.pow(2, d));
drawNodes.push({ val: nd.val, left: nd.left, right: nd.right, depth: d, drawX: x, drawY: d }); drawNodes.push({ val: nd.val, left: nd.left, right: nd.right, depth: d, drawX: x, drawY: d });
} }
return { type: 'heap', nodes: drawNodes, totalWidth: totalWidth, maxDepth: maxD, highlight: highlight || [] }; return { type: 'heap', nodes: drawNodes, totalWidth: totalWidth, maxDepth: maxD, highlight: highlight || [] };
@ -1646,8 +1646,8 @@ function buildSteps(algoName) {
assignX(drawNodes[idx].right); assignX(drawNodes[idx].right);
} }
assignX(0); assignX(0);
var maxD = 0; let maxD = 0;
for (var di = 0; di < drawNodes.length; di++) { if (drawNodes[di].depth > maxD) maxD = drawNodes[di].depth; } for (let di = 0; di < drawNodes.length; di++) { if (drawNodes[di].depth > maxD) maxD = drawNodes[di].depth; }
return { type: 'bst', nodes: drawNodes, totalWidth: xCounter, maxDepth: maxD, highlight: highlightPath || [] }; return { type: 'bst', nodes: drawNodes, totalWidth: xCounter, maxDepth: maxD, highlight: highlightPath || [] };
} }
@ -1754,12 +1754,12 @@ function buildSteps(algoName) {
while (size < n) size <<= 1; while (size < n) size <<= 1;
// Padding: eindeutige Werte oberhalb des Maximums (alle > origMax → Sortierung korrekt, // Padding: eindeutige Werte oberhalb des Maximums (alle > origMax → Sortierung korrekt,
// aber unterschiedlich → Balken visuell unterscheidbar wenn sie in sichtbare Positionen geraten) // aber unterschiedlich → Balken visuell unterscheidbar wenn sie in sichtbare Positionen geraten)
var origMax = 0; let origMax = 0;
for (let i = 0; i < n; i++) { if (arr[i] > origMax) origMax = arr[i]; } for (let i = 0; i < n; i++) { if (arr[i] > origMax) origMax = arr[i]; }
const bArr = arr.slice(); const bArr = arr.slice();
var padLo = origMax * 1.05 + 1; let padLo = origMax * 1.05 + 1;
var padHi = origMax * 1.25 + 1; let padHi = origMax * 1.25 + 1;
var padCount = size - n; let padCount = size - n;
for (let i = n; i < size; i++) { for (let i = n; i < size; i++) {
bArr.push(padCount > 1 ? padLo + (padHi - padLo) * (i - n) / (padCount - 1) : padLo); bArr.push(padCount > 1 ? padLo + (padHi - padLo) * (i - n) / (padCount - 1) : padLo);
} }
@ -1768,9 +1768,9 @@ function buildSteps(algoName) {
const allStages = []; const allStages = [];
function collectMergeStages(lo, cnt) { function collectMergeStages(lo, cnt) {
if (cnt <= 1) return; if (cnt <= 1) return;
var half = cnt >> 1; let half = cnt >> 1;
var stage = []; let stage = [];
for (var i = lo; i < lo + half; i++) { for (let i = lo; i < lo + half; i++) {
if (i < n && (i + half) < n) stage.push([i, i + half]); if (i < n && (i + half) < n) stage.push([i, i + half]);
} }
if (stage.length > 0) allStages.push(stage); if (stage.length > 0) allStages.push(stage);
@ -1779,7 +1779,7 @@ function buildSteps(algoName) {
} }
function collectSortStages(lo, cnt) { function collectSortStages(lo, cnt) {
if (cnt <= 1) return; if (cnt <= 1) return;
var half = cnt >> 1; let half = cnt >> 1;
collectSortStages(lo, half); collectSortStages(lo, half);
collectSortStages(lo + half, half); collectSortStages(lo + half, half);
collectMergeStages(lo, cnt); collectMergeStages(lo, cnt);
@ -1787,12 +1787,12 @@ function buildSteps(algoName) {
collectSortStages(0, size); collectSortStages(0, size);
// Stage-Index: synchron mit bitonicMerge hochgezählt // Stage-Index: synchron mit bitonicMerge hochgezählt
var snapStageId = 0; let snapStageId = 0;
// Snap-Wrapper: zeigt nur die ersten n Elemente + Netz-Zustand // Snap-Wrapper: zeigt nur die ersten n Elemente + Netz-Zustand
function bSnap(type, indices) { function bSnap(type, indices) {
var visIdx = indices.filter(function(idx) { return idx < n; }); let visIdx = indices.filter(function(idx) { return idx < n; });
for (var k = 0; k < n; k++) arr[k] = bArr[k]; for (let k = 0; k < n; k++) arr[k] = bArr[k];
out.push({ out.push({
type: type, indices: visIdx, array: arr.slice(), type: type, indices: visIdx, array: arr.slice(),
compares: compares, swaps: swaps, moves: moves, compares: compares, swaps: swaps, moves: moves,
@ -1806,7 +1806,7 @@ function buildSteps(algoName) {
bSnap('compare', [i, j]); bSnap('compare', [i, j]);
} }
if ((bArr[i] > bArr[j]) === dir) { if ((bArr[i] > bArr[j]) === dir) {
var tmp = bArr[i]; bArr[i] = bArr[j]; bArr[j] = tmp; let tmp = bArr[i]; bArr[i] = bArr[j]; bArr[j] = tmp;
if (i < n && j < n) { if (i < n && j < n) {
swaps++; swaps++;
bSnap('swap', [i, j]); bSnap('swap', [i, j]);
@ -1815,9 +1815,9 @@ function buildSteps(algoName) {
} }
function bitonicMerge(lo, cnt, dir) { function bitonicMerge(lo, cnt, dir) {
if (cnt <= 1) return; if (cnt <= 1) return;
var half = cnt >> 1; let half = cnt >> 1;
var hasVisible = false; let hasVisible = false;
for (var i = lo; i < lo + half; i++) { for (let i = lo; i < lo + half; i++) {
if (i < n && (i + half) < n) hasVisible = true; if (i < n && (i + half) < n) hasVisible = true;
bitonicCompareSwap(i, i + half, dir); bitonicCompareSwap(i, i + half, dir);
} }
@ -1827,14 +1827,14 @@ function buildSteps(algoName) {
} }
function bitonicSortRec(lo, cnt, dir) { function bitonicSortRec(lo, cnt, dir) {
if (cnt <= 1) return; if (cnt <= 1) return;
var half = cnt >> 1; let half = cnt >> 1;
bitonicSortRec(lo, half, true); bitonicSortRec(lo, half, true);
bitonicSortRec(lo + half, half, false); bitonicSortRec(lo + half, half, false);
bitonicMerge(lo, cnt, dir); bitonicMerge(lo, cnt, dir);
} }
bitonicSortRec(0, size, true); bitonicSortRec(0, size, true);
// Ergebnis zurückkopieren (nur die ersten n Elemente, Padding verwerfen) // Ergebnis zurückkopieren (nur die ersten n Elemente, Padding verwerfen)
for (var i = 0; i < n; i++) arr[i] = bArr[i]; for (let i = 0; i < n; i++) arr[i] = bArr[i];
break; break;
} }
@ -2061,13 +2061,13 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
} }
if (meta.sortedFrom !== undefined && meta.sortedFrom < n) { if (meta.sortedFrom !== undefined && meta.sortedFrom < n) {
ctx.fillStyle = 'rgba(60, 255, 160, 0.06)'; ctx.fillStyle = 'rgba(60, 255, 160, 0.06)';
var sx = PAD_L + meta.sortedFrom * slotW; let sx = PAD_L + meta.sortedFrom * slotW;
ctx.fillRect(sx, PAD_T - 2, (n - meta.sortedFrom) * slotW, areaH + 4); ctx.fillRect(sx, PAD_T - 2, (n - meta.sortedFrom) * slotW, areaH + 4);
} }
// Aktiver Bereich (dünne Linie oben) // Aktiver Bereich (dünne Linie oben)
if (meta.activeRange) { if (meta.activeRange) {
var ax0 = PAD_L + meta.activeRange.lo * slotW + gapW / 2; let ax0 = PAD_L + meta.activeRange.lo * slotW + gapW / 2;
var ax1 = PAD_L + meta.activeRange.hi * slotW + gapW / 2 + barW; let ax1 = PAD_L + meta.activeRange.hi * slotW + gapW / 2 + barW;
ctx.save(); ctx.save();
ctx.strokeStyle = 'rgba(96, 165, 250, 0.5)'; ctx.strokeStyle = 'rgba(96, 165, 250, 0.5)';
ctx.lineWidth = 2; ctx.lineWidth = 2;
@ -2084,12 +2084,12 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
} }
// Partitionsbereiche (subtile Hintergrundfarben) // Partitionsbereiche (subtile Hintergrundfarben)
if (meta.partitions) { if (meta.partitions) {
var partColors = ['rgba(96,165,250,0.05)', 'rgba(255,154,60,0.05)', 'rgba(185,74,255,0.05)']; let partColors = ['rgba(96,165,250,0.05)', 'rgba(255,154,60,0.05)', 'rgba(185,74,255,0.05)'];
for (var pi = 0; pi < meta.partitions.length; pi++) { for (let pi = 0; pi < meta.partitions.length; pi++) {
var part = meta.partitions[pi]; let part = meta.partitions[pi];
ctx.fillStyle = partColors[pi % partColors.length]; ctx.fillStyle = partColors[pi % partColors.length];
var px0 = PAD_L + part.lo * slotW; let px0 = PAD_L + part.lo * slotW;
var px1 = PAD_L + (part.hi + 1) * slotW; let px1 = PAD_L + (part.hi + 1) * slotW;
ctx.fillRect(px0, PAD_T - 2, px1 - px0, areaH + 4); ctx.fillRect(px0, PAD_T - 2, px1 - px0, areaH + 4);
} }
} }
@ -2097,8 +2097,8 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
if (meta.runs) { if (meta.runs) {
ctx.strokeStyle = 'rgba(255, 255, 255, 0.18)'; ctx.strokeStyle = 'rgba(255, 255, 255, 0.18)';
ctx.lineWidth = 1; ctx.lineWidth = 1;
for (var ri = 0; ri < meta.runs.length; ri++) { for (let ri = 0; ri < meta.runs.length; ri++) {
var rx = PAD_L + meta.runs[ri] * slotW + 0.5; let rx = PAD_L + meta.runs[ri] * slotW + 0.5;
ctx.beginPath(); ctx.beginPath();
ctx.moveTo(rx, PAD_T - 2); ctx.moveTo(rx, PAD_T - 2);
ctx.lineTo(rx, H - PAD_B); ctx.lineTo(rx, H - PAD_B);
@ -2182,12 +2182,12 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
if (meta.pivots) { if (meta.pivots) {
ctx.strokeStyle = '#ffffff'; ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 2; ctx.lineWidth = 2;
for (var pvi = 0; pvi < meta.pivots.length; pvi++) { for (let pvi = 0; pvi < meta.pivots.length; pvi++) {
var pvIdx = meta.pivots[pvi]; let pvIdx = meta.pivots[pvi];
if (pvIdx >= 0 && pvIdx < n) { if (pvIdx >= 0 && pvIdx < n) {
var pvBarH = Math.max(2, (arr[pvIdx] / maxVal) * areaH); let pvBarH = Math.max(2, (arr[pvIdx] / maxVal) * areaH);
var pvX = PAD_L + pvIdx * slotW + gapW / 2; let pvX = PAD_L + pvIdx * slotW + gapW / 2;
var pvY = H - PAD_B - pvBarH; let pvY = H - PAD_B - pvBarH;
ctx.strokeRect(pvX - 1, pvY - 1, barW + 2, pvBarH + 2); ctx.strokeRect(pvX - 1, pvY - 1, barW + 2, pvBarH + 2);
} }
} }
@ -2251,8 +2251,8 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
function drawMemory(state) { function drawMemory(state) {
if (!state) return; if (!state) return;
var dpr = window.devicePixelRatio || 1; let dpr = window.devicePixelRatio || 1;
var W = memCanvas.width / dpr, H = memCanvas.height / dpr; let W = memCanvas.width / dpr, H = memCanvas.height / dpr;
memCtx.clearRect(0, 0, W, H); memCtx.clearRect(0, 0, W, H);
if (state.type === 'bst') drawBST(state, W, H); if (state.type === 'bst') drawBST(state, W, H);
else if (state.type === 'heap') drawHeap(state, W, H); else if (state.type === 'heap') drawHeap(state, W, H);
@ -2264,33 +2264,33 @@ function drawMemory(state) {
} }
function drawBST(state, W, H) { function drawBST(state, W, H) {
var nodes = state.nodes, totalWidth = state.totalWidth, maxDepth = state.maxDepth, highlight = state.highlight; let nodes = state.nodes, totalWidth = state.totalWidth, maxDepth = state.maxDepth, highlight = state.highlight;
if (!nodes || nodes.length === 0) return; if (!nodes || nodes.length === 0) return;
var PAD = 40, aW = W - PAD * 2, aH = H - PAD * 2; let PAD = 40, aW = W - PAD * 2, aH = H - PAD * 2;
var stepX = totalWidth > 1 ? aW / (totalWidth - 1) : 0; let stepX = totalWidth > 1 ? aW / (totalWidth - 1) : 0;
var stepY = maxDepth > 0 ? aH / maxDepth : 0; let stepY = maxDepth > 0 ? aH / maxDepth : 0;
// Knoten-Radius skaliert mit Anzahl // Knoten-Radius skaliert mit Anzahl
var R = Math.max(6, Math.min(20, stepX * 0.4)); let R = Math.max(6, Math.min(20, stepX * 0.4));
// Kanten zeichnen // Kanten zeichnen
memCtx.strokeStyle = '#3a3f55'; memCtx.lineWidth = 2; memCtx.strokeStyle = '#3a3f55'; memCtx.lineWidth = 2;
for (var i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
var p = nodes[i], px = PAD + p.drawX * stepX, py = PAD + p.drawY * stepY; let p = nodes[i], px = PAD + p.drawX * stepX, py = PAD + p.drawY * stepY;
var children = [p.left, p.right]; let children = [p.left, p.right];
for (var ci = 0; ci < children.length; ci++) { for (let ci = 0; ci < children.length; ci++) {
var cIdx = children[ci]; let cIdx = children[ci];
if (cIdx !== -1 && nodes[cIdx]) { if (cIdx !== -1 && nodes[cIdx]) {
var c = nodes[cIdx], cx = PAD + c.drawX * stepX, cy = PAD + c.drawY * stepY; let c = nodes[cIdx], cx = PAD + c.drawX * stepX, cy = PAD + c.drawY * stepY;
memCtx.beginPath(); memCtx.moveTo(px, py); memCtx.lineTo(cx, cy); memCtx.stroke(); memCtx.beginPath(); memCtx.moveTo(px, py); memCtx.lineTo(cx, cy); memCtx.stroke();
} }
} }
} }
// Knoten zeichnen // Knoten zeichnen
for (var i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
var n = nodes[i], x = PAD + n.drawX * stepX, y = PAD + n.drawY * stepY; let n = nodes[i], x = PAD + n.drawX * stepX, y = PAD + n.drawY * stepY;
var isH = highlight.indexOf(i) !== -1; let isH = highlight.indexOf(i) !== -1;
var color = isH ? COLORS.compare : COLORS.normal; let color = isH ? COLORS.compare : COLORS.normal;
if (isH) { if (isH) {
memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 15; memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 15;
memCtx.fillStyle = color; memCtx.beginPath(); memCtx.arc(x, y, R, 0, Math.PI * 2); memCtx.fill(); memCtx.fillStyle = color; memCtx.beginPath(); memCtx.arc(x, y, R, 0, Math.PI * 2); memCtx.fill();
@ -2305,30 +2305,30 @@ function drawBST(state, W, H) {
} }
function drawMergeArrays(state, W, H) { function drawMergeArrays(state, W, H) {
var left = state.left, right = state.right, lPtr = state.lPtr, rPtr = state.rPtr; let left = state.left, right = state.right, lPtr = state.lPtr, rPtr = state.rPtr;
var totalLen = left.length + right.length; let totalLen = left.length + right.length;
if (totalLen === 0) return; if (totalLen === 0) return;
// Sicheres Maximum ohne Spread (Stack-Overflow-Schutz bei großen Arrays) // Sicheres Maximum ohne Spread (Stack-Overflow-Schutz bei großen Arrays)
var maxV = 1; let maxV = 1;
for (var mi = 0; mi < left.length; mi++) { if (left[mi] > maxV) maxV = left[mi]; } for (let mi = 0; mi < left.length; mi++) { if (left[mi] > maxV) maxV = left[mi]; }
for (var mi = 0; mi < right.length; mi++) { if (right[mi] > maxV) maxV = right[mi]; } for (let mi = 0; mi < right.length; mi++) { if (right[mi] > maxV) maxV = right[mi]; }
var PAD = 20, GAP = 20; let PAD = 20, GAP = 20;
var isNarrow = W < 400; let isNarrow = W < 400;
if (isNarrow) { if (isNarrow) {
// Mobile: Arrays untereinander // Mobile: Arrays untereinander
var sW = (W - PAD * 2) / Math.max(left.length, right.length); let sW = (W - PAD * 2) / Math.max(left.length, right.length);
var bW = sW * 0.7, gap = sW * 0.3; let bW = sW * 0.7, gap = sW * 0.3;
var halfH = (H - 40) / 2, aH = halfH * 0.55, baseY1 = halfH - 5, baseY2 = H - 10; let halfH = (H - 40) / 2, aH = halfH * 0.55, baseY1 = halfH - 5, baseY2 = H - 10;
// Links // Links
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('Links (Index ' + state.l + '\u2013' + state.m + ')', PAD, 13); memCtx.fillText('Links (Index ' + state.l + '\u2013' + state.m + ')', PAD, 13);
for (var i = 0; i < left.length; i++) { for (let i = 0; i < left.length; i++) {
var x = PAD + i * sW + gap / 2, bH = Math.max(2, (left[i] / maxV) * aH), y = baseY1 - bH; let x = PAD + i * sW + gap / 2, bH = Math.max(2, (left[i] / maxV) * aH), y = baseY1 - bH;
var isH = (i === lPtr), color = isH ? COLORS.compare : '#3a7cff'; let isH = (i === lPtr), color = isH ? COLORS.compare : '#3a7cff';
if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); } if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); }
memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH);
if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(left[i]), x + bW / 2, y - 4); } if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(left[i]), x + bW / 2, y - 4); }
@ -2337,37 +2337,37 @@ function drawMergeArrays(state, W, H) {
// Rechts // Rechts
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('Rechts (Index ' + (state.m + 1) + '\u2013' + state.r + ')', PAD, halfH + 13); memCtx.fillText('Rechts (Index ' + (state.m + 1) + '\u2013' + state.r + ')', PAD, halfH + 13);
for (var i = 0; i < right.length; i++) { for (let i = 0; i < right.length; i++) {
var x = PAD + i * sW + gap / 2, bH = Math.max(2, (right[i] / maxV) * aH), y = baseY2 - bH; let x = PAD + i * sW + gap / 2, bH = Math.max(2, (right[i] / maxV) * aH), y = baseY2 - bH;
var isH = (i === rPtr), color = isH ? COLORS.compare : '#b94aff'; let isH = (i === rPtr), color = isH ? COLORS.compare : '#b94aff';
if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); } if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); }
memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH);
if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(right[i]), x + bW / 2, y - 4); } if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(right[i]), x + bW / 2, y - 4); }
} }
} else { } else {
// Desktop: Arrays nebeneinander // Desktop: Arrays nebeneinander
var totalW = W - PAD * 2 - GAP; let totalW = W - PAD * 2 - GAP;
var sW = totalW / totalLen, bW = sW * 0.7, gap = sW * 0.3; let sW = totalW / totalLen, bW = sW * 0.7, gap = sW * 0.3;
var aH = H * 0.6, baseY = H - 20; let aH = H * 0.6, baseY = H - 20;
// Links // Links
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('Links (Index ' + state.l + '\u2013' + state.m + ')', PAD, 15); memCtx.fillText('Links (Index ' + state.l + '\u2013' + state.m + ')', PAD, 15);
for (var i = 0; i < left.length; i++) { for (let i = 0; i < left.length; i++) {
var x = PAD + i * sW + gap / 2, bH = Math.max(2, (left[i] / maxV) * aH), y = baseY - bH; let x = PAD + i * sW + gap / 2, bH = Math.max(2, (left[i] / maxV) * aH), y = baseY - bH;
var isH = (i === lPtr), color = isH ? COLORS.compare : '#3a7cff'; let isH = (i === lPtr), color = isH ? COLORS.compare : '#3a7cff';
if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); } if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); }
memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH);
if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(left[i]), x + bW / 2, y - 4); } if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(left[i]), x + bW / 2, y - 4); }
} }
// Rechts // Rechts
var rightStartX = PAD + left.length * sW + GAP; let rightStartX = PAD + left.length * sW + GAP;
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('Rechts (Index ' + (state.m + 1) + '\u2013' + state.r + ')', rightStartX, 15); memCtx.fillText('Rechts (Index ' + (state.m + 1) + '\u2013' + state.r + ')', rightStartX, 15);
for (var i = 0; i < right.length; i++) { for (let i = 0; i < right.length; i++) {
var x = rightStartX + i * sW + gap / 2, bH = Math.max(2, (right[i] / maxV) * aH), y = baseY - bH; let x = rightStartX + i * sW + gap / 2, bH = Math.max(2, (right[i] / maxV) * aH), y = baseY - bH;
var isH = (i === rPtr), color = isH ? COLORS.compare : '#b94aff'; let isH = (i === rPtr), color = isH ? COLORS.compare : '#b94aff';
if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); } if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); }
memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH);
if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(right[i]), x + bW / 2, y - 4); } if (bW > 12) { memCtx.fillStyle = '#fff'; memCtx.font = '600 ' + Math.min(10, bW * 0.6) + 'px monospace'; memCtx.textAlign = 'center'; memCtx.fillText(String(right[i]), x + bW / 2, y - 4); }
@ -2376,31 +2376,31 @@ function drawMergeArrays(state, W, H) {
} }
function drawHeap(state, W, H) { function drawHeap(state, W, H) {
var nodes = state.nodes, totalWidth = state.totalWidth, maxDepth = state.maxDepth, highlight = state.highlight; let nodes = state.nodes, totalWidth = state.totalWidth, maxDepth = state.maxDepth, highlight = state.highlight;
if (!nodes || nodes.length === 0) return; if (!nodes || nodes.length === 0) return;
var PAD = 30, aW = W - PAD * 2, aH = H - PAD * 2; let PAD = 30, aW = W - PAD * 2, aH = H - PAD * 2;
var stepY = maxDepth > 0 ? aH / maxDepth : 0; let stepY = maxDepth > 0 ? aH / maxDepth : 0;
var R = Math.max(6, Math.min(18, aW / (nodes.length + 1) * 0.35)); let R = Math.max(6, Math.min(18, aW / (nodes.length + 1) * 0.35));
// Kanten // Kanten
memCtx.strokeStyle = '#3a3f55'; memCtx.lineWidth = 1.5; memCtx.strokeStyle = '#3a3f55'; memCtx.lineWidth = 1.5;
for (var i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
var nd = nodes[i], px = PAD + (nd.drawX / totalWidth) * aW, py = PAD + nd.drawY * stepY; let nd = nodes[i], px = PAD + (nd.drawX / totalWidth) * aW, py = PAD + nd.drawY * stepY;
var children = [nd.left, nd.right]; let children = [nd.left, nd.right];
for (var ci = 0; ci < children.length; ci++) { for (let ci = 0; ci < children.length; ci++) {
var cIdx = children[ci]; let cIdx = children[ci];
if (cIdx !== -1 && nodes[cIdx]) { if (cIdx !== -1 && nodes[cIdx]) {
var c = nodes[cIdx], cx = PAD + (c.drawX / totalWidth) * aW, cy = PAD + c.drawY * stepY; let c = nodes[cIdx], cx = PAD + (c.drawX / totalWidth) * aW, cy = PAD + c.drawY * stepY;
memCtx.beginPath(); memCtx.moveTo(px, py); memCtx.lineTo(cx, cy); memCtx.stroke(); memCtx.beginPath(); memCtx.moveTo(px, py); memCtx.lineTo(cx, cy); memCtx.stroke();
} }
} }
} }
// Knoten // Knoten
for (var i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
var n = nodes[i], x = PAD + (n.drawX / totalWidth) * aW, y = PAD + n.drawY * stepY; let n = nodes[i], x = PAD + (n.drawX / totalWidth) * aW, y = PAD + n.drawY * stepY;
var isH = highlight.indexOf(i) !== -1; let isH = highlight.indexOf(i) !== -1;
var color = isH ? COLORS.compare : COLORS.normal; let color = isH ? COLORS.compare : COLORS.normal;
if (isH) { if (isH) {
memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12;
memCtx.fillStyle = color; memCtx.beginPath(); memCtx.arc(x, y, R, 0, Math.PI * 2); memCtx.fill(); memCtx.fillStyle = color; memCtx.beginPath(); memCtx.arc(x, y, R, 0, Math.PI * 2); memCtx.fill();
@ -2415,23 +2415,23 @@ function drawHeap(state, W, H) {
} }
function drawCountingArray(state, W, H) { function drawCountingArray(state, W, H) {
var count = state.count, minVal = state.minVal, hl = state.highlight; let count = state.count, minVal = state.minVal, hl = state.highlight;
var n = count.length; let n = count.length;
if (n === 0) return; if (n === 0) return;
var maxC = 1; let maxC = 1;
for (var i = 0; i < n; i++) { if (count[i] > maxC) maxC = count[i]; } for (let i = 0; i < n; i++) { if (count[i] > maxC) maxC = count[i]; }
var PAD = 20, aW = W - PAD * 2, aH = H * 0.55, baseY = H - 20; let PAD = 20, aW = W - PAD * 2, aH = H * 0.55, baseY = H - 20;
var sW = aW / n, bW = Math.max(1, sW * 0.7), gap = sW * 0.15; let sW = aW / n, bW = Math.max(1, sW * 0.7), gap = sW * 0.15;
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('H\u00e4ufigkeiten (Wert ' + minVal + '\u2013' + (minVal + n - 1) + ')', PAD, 14); memCtx.fillText('H\u00e4ufigkeiten (Wert ' + minVal + '\u2013' + (minVal + n - 1) + ')', PAD, 14);
for (var i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
var x = PAD + i * sW + gap; let x = PAD + i * sW + gap;
var bH = count[i] > 0 ? Math.max(2, (count[i] / maxC) * aH) : 0; let bH = count[i] > 0 ? Math.max(2, (count[i] / maxC) * aH) : 0;
var y = baseY - bH; let y = baseY - bH;
var isH = (i === hl); let isH = (i === hl);
var color = isH ? COLORS.move : '#3cffa0'; let color = isH ? COLORS.move : '#3cffa0';
if (bH > 0) { if (bH > 0) {
if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); } if (isH) { memCtx.save(); memCtx.shadowColor = color; memCtx.shadowBlur = 12; memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.restore(); }
memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH); memCtx.fillStyle = color; memCtx.fillRect(x, y, bW, bH);
@ -2450,29 +2450,29 @@ function drawCountingArray(state, W, H) {
} }
function drawRadixBuckets(state, W, H) { function drawRadixBuckets(state, W, H) {
var buckets = state.buckets, digitPos = state.digitPos, hl = state.highlight; let buckets = state.buckets, digitPos = state.digitPos, hl = state.highlight;
var PAD = 20, GAP = 6; let PAD = 20, GAP = 6;
var totalSlots = 10; let totalSlots = 10;
var slotW = (W - PAD * 2 - GAP * 9) / totalSlots; let slotW = (W - PAD * 2 - GAP * 9) / totalSlots;
var maxLen = 1; let maxLen = 1;
for (var d = 0; d < 10; d++) { if (buckets[d].length > maxLen) maxLen = buckets[d].length; } for (let d = 0; d < 10; d++) { if (buckets[d].length > maxLen) maxLen = buckets[d].length; }
var baseY = H - 20; let baseY = H - 20;
var itemH = Math.min(16, (H - 50) / maxLen); let itemH = Math.min(16, (H - 50) / maxLen);
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('Radix-Buckets (Stelle ' + digitPos + ')', PAD, 14); memCtx.fillText('Radix-Buckets (Stelle ' + digitPos + ')', PAD, 14);
for (var d = 0; d < 10; d++) { for (let d = 0; d < 10; d++) {
var bx = PAD + d * (slotW + GAP); let bx = PAD + d * (slotW + GAP);
var isH = (d === hl); let isH = (d === hl);
// Bucket-Label // Bucket-Label
memCtx.fillStyle = isH ? COLORS.move : '#8b949e'; memCtx.fillStyle = isH ? COLORS.move : '#8b949e';
memCtx.font = '700 11px monospace'; memCtx.textAlign = 'center'; memCtx.font = '700 11px monospace'; memCtx.textAlign = 'center';
memCtx.fillText(String(d), bx + slotW / 2, baseY + 14); memCtx.fillText(String(d), bx + slotW / 2, baseY + 14);
// Elemente als gestapelte Blöcke // Elemente als gestapelte Blöcke
for (var ei = 0; ei < buckets[d].length; ei++) { for (let ei = 0; ei < buckets[d].length; ei++) {
var y = baseY - (ei + 1) * itemH; let y = baseY - (ei + 1) * itemH;
var color = isH ? COLORS.move : COLORS.normal; let color = isH ? COLORS.move : COLORS.normal;
memCtx.fillStyle = color; memCtx.globalAlpha = 0.85; memCtx.fillStyle = color; memCtx.globalAlpha = 0.85;
memCtx.fillRect(bx + 1, y + 1, slotW - 2, itemH - 2); memCtx.fillRect(bx + 1, y + 1, slotW - 2, itemH - 2);
memCtx.globalAlpha = 1; memCtx.globalAlpha = 1;
@ -2486,35 +2486,35 @@ function drawRadixBuckets(state, W, H) {
} }
function drawBuckets(state, W, H) { function drawBuckets(state, W, H) {
var buckets = state.buckets, hl = state.highlight; let buckets = state.buckets, hl = state.highlight;
var k = buckets.length; let k = buckets.length;
if (k === 0) return; if (k === 0) return;
var PAD = 20, GAP = 4; let PAD = 20, GAP = 4;
var slotW = (W - PAD * 2 - GAP * (k - 1)) / k; let slotW = (W - PAD * 2 - GAP * (k - 1)) / k;
var maxLen = 1, maxV = 1; let maxLen = 1, maxV = 1;
for (var b = 0; b < k; b++) { for (let b = 0; b < k; b++) {
if (buckets[b].length > maxLen) maxLen = buckets[b].length; if (buckets[b].length > maxLen) maxLen = buckets[b].length;
for (var j = 0; j < buckets[b].length; j++) { if (buckets[b][j] > maxV) maxV = buckets[b][j]; } for (let j = 0; j < buckets[b].length; j++) { if (buckets[b][j] > maxV) maxV = buckets[b][j]; }
} }
var baseY = H - 20; let baseY = H - 20;
var aH = H * 0.55; let aH = H * 0.55;
memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left'; memCtx.fillStyle = '#8b949e'; memCtx.font = '600 11px Inter, sans-serif'; memCtx.textAlign = 'left';
memCtx.fillText('Buckets (' + k + ' St\u00fcck)', PAD, 14); memCtx.fillText('Buckets (' + k + ' St\u00fcck)', PAD, 14);
for (var b = 0; b < k; b++) { for (let b = 0; b < k; b++) {
var bx = PAD + b * (slotW + GAP); let bx = PAD + b * (slotW + GAP);
var isH = (b === hl); let isH = (b === hl);
// Bucket-Rahmen // Bucket-Rahmen
memCtx.strokeStyle = isH ? COLORS.move : '#3a3f55'; memCtx.lineWidth = 1; memCtx.strokeStyle = isH ? COLORS.move : '#3a3f55'; memCtx.lineWidth = 1;
memCtx.strokeRect(bx, 24, slotW, baseY - 24); memCtx.strokeRect(bx, 24, slotW, baseY - 24);
// Elemente als Balken von unten // Elemente als Balken von unten
var itemW = Math.max(1, (slotW - 4) / Math.max(1, buckets[b].length)); let itemW = Math.max(1, (slotW - 4) / Math.max(1, buckets[b].length));
for (var j = 0; j < buckets[b].length; j++) { for (let j = 0; j < buckets[b].length; j++) {
var bH = Math.max(2, (buckets[b][j] / maxV) * aH); let bH = Math.max(2, (buckets[b][j] / maxV) * aH);
var x = bx + 2 + j * itemW; let x = bx + 2 + j * itemW;
var y = baseY - bH; let y = baseY - bH;
var color = isH ? COLORS.move : COLORS.normal; let color = isH ? COLORS.move : COLORS.normal;
memCtx.fillStyle = color; memCtx.fillRect(x, y, Math.max(1, itemW - 1), bH); memCtx.fillStyle = color; memCtx.fillRect(x, y, Math.max(1, itemW - 1), bH);
} }
// Bucket-Nummer // Bucket-Nummer
@ -2527,17 +2527,17 @@ function drawBuckets(state, W, H) {
} }
function drawBitonicNetwork(state, W, H) { function drawBitonicNetwork(state, W, H) {
var stages = state.stages, cur = state.currentStage, n = state.n; let stages = state.stages, cur = state.currentStage, n = state.n;
var numS = stages.length; let numS = stages.length;
if (numS === 0 || n < 2) return; if (numS === 0 || n < 2) return;
var PAD_L = 24, PAD_R = 8, PAD_T = 10, PAD_B = 10; let PAD_L = 24, PAD_R = 8, PAD_T = 10, PAD_B = 10;
var wireSpacing = (H - PAD_T - PAD_B) / Math.max(n - 1, 1); let wireSpacing = (H - PAD_T - PAD_B) / Math.max(n - 1, 1);
var stageW = (W - PAD_L - PAD_R) / numS; let stageW = (W - PAD_L - PAD_R) / numS;
// Horizontale Drähte // Horizontale Drähte
for (var w = 0; w < n; w++) { for (let w = 0; w < n; w++) {
var wy = PAD_T + w * wireSpacing; let wy = PAD_T + w * wireSpacing;
memCtx.strokeStyle = 'rgba(100,120,160,0.3)'; memCtx.strokeStyle = 'rgba(100,120,160,0.3)';
memCtx.lineWidth = 1; memCtx.lineWidth = 1;
memCtx.beginPath(); memCtx.beginPath();
@ -2551,24 +2551,24 @@ function drawBitonicNetwork(state, W, H) {
memCtx.font = '500 9px Inter, sans-serif'; memCtx.font = '500 9px Inter, sans-serif';
memCtx.textAlign = 'right'; memCtx.textAlign = 'right';
memCtx.textBaseline = 'middle'; memCtx.textBaseline = 'middle';
for (var w = 0; w < n; w++) { for (let w = 0; w < n; w++) {
memCtx.fillText(String(w), PAD_L - 4, PAD_T + w * wireSpacing); memCtx.fillText(String(w), PAD_L - 4, PAD_T + w * wireSpacing);
} }
// Komparatoren // Komparatoren
for (var s = 0; s < numS; s++) { for (let s = 0; s < numS; s++) {
var cx = PAD_L + (s + 0.5) * stageW; let cx = PAD_L + (s + 0.5) * stageW;
var isCur = (s === cur); let isCur = (s === cur);
var isPast = (s < cur); let isPast = (s < cur);
var color = isCur ? COLORS.compare let color = isCur ? COLORS.compare
: isPast ? 'rgba(60,160,255,0.35)' : isPast ? 'rgba(60,160,255,0.35)'
: 'rgba(150,170,200,0.15)'; : 'rgba(150,170,200,0.15)';
var lw = isCur ? 2 : 1; let lw = isCur ? 2 : 1;
var R = isCur ? 3 : 2; let R = isCur ? 3 : 2;
for (var ci = 0; ci < stages[s].length; ci++) { for (let ci = 0; ci < stages[s].length; ci++) {
var a = stages[s][ci][0], b = stages[s][ci][1]; let a = stages[s][ci][0], b = stages[s][ci][1];
var y1 = PAD_T + a * wireSpacing, y2 = PAD_T + b * wireSpacing; let y1 = PAD_T + a * wireSpacing, y2 = PAD_T + b * wireSpacing;
if (isCur) { if (isCur) {
memCtx.save(); memCtx.save();
memCtx.shadowColor = COLORS.compare; memCtx.shadowColor = COLORS.compare;
@ -2635,11 +2635,11 @@ function updateMemView(memState) {
// ================================================================ // ================================================================
function renderStep(step, stepNum) { function renderStep(step, stepNum) {
var hi = {}; let hi = {};
step.indices.forEach(function(i) { hi[i] = step.type; }); step.indices.forEach(function(i) { hi[i] = step.type; });
var allSorted = step.type === 'done'; let allSorted = step.type === 'done';
// Build connected pair for connection lines // Build connected pair for connection lines
var connectedPair = null; let connectedPair = null;
if (!allSorted && step.indices.length >= 2 && (step.type === 'compare' || step.type === 'swap')) { if (!allSorted && step.indices.length >= 2 && (step.type === 'compare' || step.type === 'swap')) {
connectedPair = { indices: [step.indices[0], step.indices[1]], type: step.type }; connectedPair = { indices: [step.indices[0], step.indices[1]], type: step.type };
} }