Fix: timsort snap params, passive resize, drawPath optimization, aria-live, readUrlState labels
This commit is contained in:
parent
6950c4d788
commit
113b8ff758
1 changed files with 34 additions and 29 deletions
|
|
@ -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.5</span>
|
Sortier-Algorithmen <span style="font-size: 0.45em; font-weight: 400; opacity: 0.5; vertical-align: middle;">v0.2.6</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>
|
||||||
|
|
@ -359,8 +359,8 @@
|
||||||
|
|
||||||
<!-- Phase Label + Step Explanation (fixed height to prevent layout shift) -->
|
<!-- Phase Label + Step Explanation (fixed height to prevent layout shift) -->
|
||||||
<div class="bg-surface border border-border rounded-xl px-4 py-2 space-y-0.5" style="min-height: 52px;">
|
<div class="bg-surface border border-border rounded-xl px-4 py-2 space-y-0.5" style="min-height: 52px;">
|
||||||
<div id="phaseLabel" class="text-xs font-semibold text-accent truncate" style="min-height: 20px;"> </div>
|
<div id="phaseLabel" aria-live="polite" class="text-xs font-semibold text-accent truncate" style="min-height: 20px;"> </div>
|
||||||
<div id="stepExplanation" class="text-xs text-muted truncate font-mono" style="min-height: 18px;"> </div>
|
<div id="stepExplanation" aria-live="polite" class="text-xs text-muted truncate font-mono" style="min-height: 18px;"> </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Canvas -->
|
<!-- Canvas -->
|
||||||
|
|
@ -381,7 +381,7 @@
|
||||||
<div class="progress-track flex-1">
|
<div class="progress-track flex-1">
|
||||||
<div id="progressFill" class="progress-fill" style="width: 0%;"></div>
|
<div id="progressFill" class="progress-fill" style="width: 0%;"></div>
|
||||||
</div>
|
</div>
|
||||||
<span id="stepCounter" class="text-xs text-muted font-mono whitespace-nowrap">0 / 0</span>
|
<span id="stepCounter" aria-live="polite" class="text-xs text-muted font-mono whitespace-nowrap">0 / 0</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Statistics -->
|
<!-- Statistics -->
|
||||||
|
|
@ -1687,6 +1687,8 @@ function buildSteps(algoName) {
|
||||||
case 'timsort': {
|
case 'timsort': {
|
||||||
const n = arr.length;
|
const n = arr.length;
|
||||||
const RUN = 32;
|
const RUN = 32;
|
||||||
|
const runMeta = { runs: [] };
|
||||||
|
for (let r = 0; r < n; r += RUN) runMeta.runs.push(r);
|
||||||
// Phase 1: Insertion Sort auf Runs der Größe RUN
|
// Phase 1: Insertion Sort auf Runs der Größe RUN
|
||||||
for (let start = 0; start < n; start += RUN) {
|
for (let start = 0; start < n; start += RUN) {
|
||||||
const end = Math.min(start + RUN, n);
|
const end = Math.min(start + RUN, n);
|
||||||
|
|
@ -1695,11 +1697,11 @@ function buildSteps(algoName) {
|
||||||
let j = i - 1;
|
let j = i - 1;
|
||||||
while (j >= start) {
|
while (j >= start) {
|
||||||
compares++;
|
compares++;
|
||||||
snap('compare', [j, j + 1]);
|
snap('compare', [j, j + 1], null, runMeta);
|
||||||
if (arr[j] > key) {
|
if (arr[j] > key) {
|
||||||
arr[j + 1] = arr[j];
|
arr[j + 1] = arr[j];
|
||||||
moves++;
|
moves++;
|
||||||
snap('move', [j + 1]);
|
snap('move', [j + 1], null, runMeta);
|
||||||
j--;
|
j--;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
@ -1708,7 +1710,7 @@ function buildSteps(algoName) {
|
||||||
if (arr[j + 1] !== key) {
|
if (arr[j + 1] !== key) {
|
||||||
arr[j + 1] = key;
|
arr[j + 1] = key;
|
||||||
moves++;
|
moves++;
|
||||||
snap('move', [j + 1]);
|
snap('move', [j + 1], null, runMeta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2109,6 +2111,24 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
|
||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'bottom';
|
ctx.textBaseline = 'bottom';
|
||||||
|
|
||||||
|
// Reusable rounded-rect path helper (defined once, called per bar)
|
||||||
|
function drawRoundedRectPath(ctx, px, py, pw, ph, pr) {
|
||||||
|
if (pr > 0 && ph > pr * 2) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(px + pr, py);
|
||||||
|
ctx.lineTo(px + pw - pr, py);
|
||||||
|
ctx.quadraticCurveTo(px + pw, py, px + pw, py + pr);
|
||||||
|
ctx.lineTo(px + pw, py + ph);
|
||||||
|
ctx.lineTo(px, py + ph);
|
||||||
|
ctx.lineTo(px, py + pr);
|
||||||
|
ctx.quadraticCurveTo(px, py, px + pr, py);
|
||||||
|
ctx.closePath();
|
||||||
|
} else {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(px, py, pw, ph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < n; i++) {
|
for (let i = 0; i < n; i++) {
|
||||||
const val = arr[i];
|
const val = arr[i];
|
||||||
const barH = Math.max(2, (val / maxVal) * areaH);
|
const barH = Math.max(2, (val / maxVal) * areaH);
|
||||||
|
|
@ -2126,41 +2146,24 @@ function drawBars(arr, highlights, allSorted, meta, connectedPair) {
|
||||||
else if (t === 'move') color = COLORS.move;
|
else if (t === 'move') color = COLORS.move;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rounded-top bar path generator
|
|
||||||
const r = Math.min(3, barW / 4);
|
const r = Math.min(3, barW / 4);
|
||||||
function drawPath() {
|
|
||||||
if (r > 0 && barH > r * 2) {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.moveTo(x + r, y);
|
|
||||||
ctx.lineTo(x + barW - r, y);
|
|
||||||
ctx.quadraticCurveTo(x + barW, y, x + barW, y + r);
|
|
||||||
ctx.lineTo(x + barW, y + barH);
|
|
||||||
ctx.lineTo(x, y + barH);
|
|
||||||
ctx.lineTo(x, y + r);
|
|
||||||
ctx.quadraticCurveTo(x, y, x + r, y);
|
|
||||||
ctx.closePath();
|
|
||||||
} else {
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.rect(x, y, barW, barH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIX: Glow-Effekt VOR dem normalen Balken zeichnen
|
// Glow-Effekt
|
||||||
if (highlights[i] && !allSorted) {
|
if (highlights[i] && !allSorted) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.shadowColor = color;
|
ctx.shadowColor = color;
|
||||||
ctx.shadowBlur = 14;
|
ctx.shadowBlur = 14;
|
||||||
ctx.globalAlpha = 0.35;
|
ctx.globalAlpha = 0.35;
|
||||||
drawPath();
|
drawRoundedRectPath(ctx, x, y, barW, barH, r);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Haupt-Balken zeichnen
|
// Haupt-Balken
|
||||||
ctx.fillStyle = color;
|
ctx.fillStyle = color;
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
drawPath();
|
drawRoundedRectPath(ctx, x, y, barW, barH, r);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
// Value label
|
// Value label
|
||||||
|
|
@ -2929,6 +2932,8 @@ function readUrlState() {
|
||||||
if (params.has('preset')) $presetSelect.value = params.get('preset');
|
if (params.has('preset')) $presetSelect.value = params.get('preset');
|
||||||
if (params.has('size')) $sizeSlider.value = Math.max(5, Math.min(200, parseInt(params.get('size'), 10) || 50));
|
if (params.has('size')) $sizeSlider.value = Math.max(5, Math.min(200, parseInt(params.get('size'), 10) || 50));
|
||||||
if (params.has('speed')) $speedSlider.value = Math.max(1, Math.min(100, parseInt(params.get('speed'), 10) || 50));
|
if (params.has('speed')) $speedSlider.value = Math.max(1, Math.min(100, parseInt(params.get('speed'), 10) || 50));
|
||||||
|
updateSizeLabel();
|
||||||
|
updateSpeedLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeUrlState() {
|
function writeUrlState() {
|
||||||
|
|
@ -2961,7 +2966,7 @@ function handleResize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use window resize to avoid feedback loops (ResizeObserver on canvas parent causes blowout)
|
// Use window resize to avoid feedback loops (ResizeObserver on canvas parent causes blowout)
|
||||||
window.addEventListener('resize', handleResize);
|
window.addEventListener('resize', handleResize, { passive: true });
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
// Initialization
|
// Initialization
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue