From 09273e9338079565a3b3bd40b8e90145e51cfb56 Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Tue, 29 Apr 2025 01:35:32 -0400 Subject: [PATCH] updating max heap animation --- .../max_heap/{animation.js => max_heap.js} | 338 ++++++++++++------ 1 file changed, 219 insertions(+), 119 deletions(-) rename animations/priority_queue/max_heap/{animation.js => max_heap.js} (60%) diff --git a/animations/priority_queue/max_heap/animation.js b/animations/priority_queue/max_heap/max_heap.js similarity index 60% rename from animations/priority_queue/max_heap/animation.js rename to animations/priority_queue/max_heap/max_heap.js index 3932818..b258da5 100644 --- a/animations/priority_queue/max_heap/animation.js +++ b/animations/priority_queue/max_heap/max_heap.js @@ -7,11 +7,10 @@ var stage = new Konva.Stage({ var layer = new Konva.Layer(); stage.add(layer); - var heap = []; var nodeRadius = 25; var levelHeight = 80; -var startX = 500; +var startX = 100; var startY = 50; var horizontalSpacing = 150; @@ -32,112 +31,6 @@ var operations = [ { type: 'pop' } ]; -function drawNode(x, y, value, color = '#ffffff') { - var group = new Konva.Group({ x: x, y: y }); - - var circle = new Konva.Circle({ - radius: nodeRadius, - fill: color, - stroke: '#000000', - strokeWidth: 2 - }); - - var text = new Konva.Text({ - text: value.toString(), - fill: 'black', - align: 'center', - width: nodeRadius * 2, - x: -nodeRadius, - y: -10 - }); - - group.add(circle); - group.add(text); - layer.add(group); - animationNodes.push(group); - - return group; -} - -function makeLine(x1, y1, x2, y2) { - var line = new Konva.Line({ - points: [x1, y1, x2, y2], - stroke: 'black', - strokeWidth: 2, - lineCap: 'round', - lineJoin: 'round' - }); - layer.add(line); - animationLines.push(line); - return line; -} - -function makeTree() { - animationNodes.forEach(n => n.destroy()); - animationLines.forEach(l => l.destroy()); - animationNodes = []; - animationLines = []; - - if (heap.length === 0) return; - - var positions = []; - - for (var i = 0; i < heap.length; i++) { - var level = Math.floor(Math.log2(i + 1)); - var indexInLevel = i - (2 ** level - 1); - var nodesInLevel = 2 ** level; - - var x = startX + (indexInLevel - (nodesInLevel - 1) / 2) * horizontalSpacing * (1.5 - level / 5); - var y = startY + level * levelHeight; - - positions.push({ x: x, y: y }); - drawNode(x, y, heap[i]); - - if (i > 0) { - var parentPos = positions[Math.floor((i - 1) / 2)]; - makeLine(parentPos.x, parentPos.y + nodeRadius, x, y - nodeRadius); - } - } - - layer.draw(); -} - -function push(val) { - heap.push(val); - var idx = heap.length - 1; - while (idx > 0) { - var parentIdx = Math.floor((idx - 1) / 2); - if (heap[parentIdx] >= heap[idx]) break; - [heap[parentIdx], heap[idx]] = [heap[idx], heap[parentIdx]]; - idx = parentIdx; - } -} - -function pop() { - if (heap.length === 0) return null; - - var max = heap[0]; - heap[0] = heap[heap.length - 1]; - heap.pop(); - - var idx = 0; - while (true) { - var left = 2 * idx + 1; - var right = 2 * idx + 2; - var largest = idx; - - if (left < heap.length && heap[left] > heap[largest]) largest = left; - if (right < heap.length && heap[right] > heap[largest]) largest = right; - - if (largest === idx) break; - - [heap[idx], heap[largest]] = [heap[largest], heap[idx]]; - idx = largest; - } - - return max; -} - var cppCode = [ "#include ", "#include ", @@ -161,28 +54,235 @@ var cppCode = [ "}" ]; -function renderCode(step) { - var codeBlock = document.getElementById('codeBlock'); - codeBlock.innerHTML = cppCode.map((line, i) => { - var bold = false; - if (step >= 0 && step <= 4) bold = i === 6 + step; - else if (step >= 5 && step <= 10) bold = i === 13 || i === 14; - return bold ? `${line}` : line; - }).join('\n'); +var codeBox = new Konva.Rect({ + x: 20, + y: 60, + width: 500, + height: 550, + stroke: '#555', + strokeWidth: 5, + fill: '#ddd', + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); +layer.add(codeBox); + +var consoleBox = new Konva.Rect({ + x: 550, + y: 60, + width: 200, + height: 350, + stroke: '#555', + strokeWidth: 5, + fill: '#ddd', + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); +layer.add(consoleBox); + +var consoleLabel = new Konva.Text({ + x: 610, + y: 30, + text: "Console", + fontSize: 24, + fontFamily: 'Calibri', + fill: '#000', +}); +layer.add(consoleLabel); + +var consoleOutputText = new Konva.Text({ + x: 570, + y: 80, + text: "", + fontSize: 20, + fontFamily: 'Calibri', + fill: 'black', + width: 360, + wrap: 'word' +}); +layer.add(consoleOutputText); + +var consoleContent = ""; + +function makeCodeLine(x, y, text, id) { + return new Konva.Text({ + x: x, + y: y, + text: text, + id: 'line' + id, + fontSize: 18, + fontFamily: 'Calibri', + fill: '#000000', + width: 460, + padding: 5, + }); } + +for (let i = 0; i < cppCode.length; i++) { + let t = makeCodeLine(30, 70 + i * 25, cppCode[i], i + 1); + layer.add(t); +} + +function makeBold(id) { + for (let i = 1; i <= cppCode.length; i++) { + let line = stage.findOne('#line' + i); + if (line) { + if (i === id) { + line.fontStyle('bold'); + } else { + line.fontStyle('normal'); + } + } + } +} + +function renderCode(step) { + if (step >= 0 && step <= 4) { + makeBold(7 + step); + } else if (step >= 5 && step <= 10) { + makeBold(14); + } else { + makeBold(0); + } +} + +function drawNode(x, y, value, color = '#ffffff') { + var group = new Konva.Group({ x: x, y: y }); + + var circle = new Konva.Circle({ + radius: nodeRadius, + fill: color, + stroke: '#555', + strokeWidth: 5, + fill: '#ddd', + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + }); + + var text = new Konva.Text({ + text: value.toString(), + fill: 'black', + align: 'center', + width: nodeRadius * 2, + x: -nodeRadius, + y: -7 + }); + + group.add(circle); + group.add(text); + layer.add(group); + animationNodes.push(group); + + return group; +} + +function makeLine(x1, y1, x2, y2) { + var line = new Konva.Line({ + points: [x1, y1, x2, y2], + stroke: '#555', + strokeWidth: 2, + lineCap: 'round', + lineJoin: 'round' + }); + layer.add(line); + animationLines.push(line); + return line; +} + +function makeTree() { + animationNodes.forEach(n => n.destroy()); + animationLines.forEach(l => l.destroy()); + animationNodes = []; + animationLines = []; + + if (heap.length === 0) return; + var positions = []; + + for (var i = 0; i < heap.length; i++) { + var level = Math.floor(Math.log2(i + 1)); + var indexInLevel = i - (2 ** level - 1); + var nodesInLevel = 2 ** level; + var x = startX + (indexInLevel - (nodesInLevel - 1) / 2) * horizontalSpacing * (1.5 - level / 5); + var y = startY + level * levelHeight; + + positions.push({ x: x, y: y }); + drawNode(x, y, heap[i]); + + if (i > 0) { + var parentPos = positions[Math.floor((i - 1) / 2)]; + makeLine(parentPos.x, parentPos.y + nodeRadius, x, y - nodeRadius); + } + } + layer.draw(); +} + +function push(val) { + heap.push(val); + var idx = heap.length - 1; + while (idx > 0) { + var parentIdx = Math.floor((idx - 1) / 2); + if (heap[parentIdx] >= heap[idx]) break; + [heap[parentIdx], heap[idx]] = [heap[idx], heap[parentIdx]]; + idx = parentIdx; + } +} + +function pop() { + if (heap.length === 0) { return null; } + + var max = heap[0]; + heap[0] = heap[heap.length - 1]; + heap.pop(); + + var idx = 0; + while (true) { + var left = 2 * idx + 1; + var right = 2 * idx + 2; + var largest = idx; + + if (left < heap.length && heap[left] > heap[largest]) largest = left; + if (right < heap.length && heap[right] > heap[largest]) largest = right; + + if (largest === idx) break; + + [heap[idx], heap[largest]] = [heap[largest], heap[idx]]; + idx = largest; + } + + return max; +} + function next() { + if (currentStep >= operations.length) { + alert("End of animation! Refresh the page if you want to re-run the animation."); + return; + } + var op = operations[currentStep]; if (op.type === 'push') { push(op.value); } else if (op.type === 'pop') { - pop(); + let val = pop(); + if (val !== null) { + consoleContent += val + ' '; + consoleOutputText.text(consoleContent); + } } - renderCode(currentStep); makeTree(); currentStep++; } document.getElementById('next').addEventListener('click', next); - layer.draw();