From 506ab002007b3199b174d9652ac0f4f15dfbe9fa Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Tue, 29 Apr 2025 01:31:22 -0400 Subject: [PATCH] updating hash table animation --- animations/hash_tables/main.js | 1393 +++++++++++++++++--------------- 1 file changed, 734 insertions(+), 659 deletions(-) diff --git a/animations/hash_tables/main.js b/animations/hash_tables/main.js index ee35b9e..5038b6f 100644 --- a/animations/hash_tables/main.js +++ b/animations/hash_tables/main.js @@ -1,659 +1,734 @@ -// Author: Xi Chen -// first we need to create a stage -var stage = new Konva.Stage({ - container: 'container', // id of container
- width: 2000, - height: 1000 -}); - -// then create layer -var layer = new Konva.Layer(); - -function makeMemory(xstart, ystart, bufferSize, w, h) -{ - for (let i = 0; i < bufferSize; ++i) - { - let tr = new Konva.Rect - ({ - x: xstart, - y: ystart+i*h, - id:"brec"+i, - stroke: '#343434', - strokeWidth: 5, - fill: '#ddd', - width: w, - height: h, - shadowColor: 'black', - shadowBlur: 10, - shadowOffsetX: 10, - shadowOffsetY: 10, - shadowOpacity: 0.2, - }); - - layer.add(tr); - - // Add index number to the left of each box - let indexText = new Konva.Text - ({ - x: xstart - 30, // Position to the left of the box - y: ystart + i*h + h/2 - 10, // Center vertically - text: i.toString(), - fontSize: 18, - fontFamily: 'Calibri', - fill: '#000000', - align: 'center', - width: 30, - }); - - layer.add(indexText); - } // end of for loop -} - -makeMemory(1650,60,10,50,50); - -var memoryText = new Konva.Text -({ - x: 1625, - y: 25, - id:"memory", - text: "Memory", - fontSize: 28, - fontFamily: 'Calibri', - fill: '#000000', -}); - -var mainLabel = new Konva.Text -({ - x: 400, - y: 0, - id:"main", - text: "Main", - fontSize: 28, - fontFamily: 'Calibri', - fill: '#000000', -}); - -// box for code -layer.add(memoryText); -layer.add(mainLabel); - -// add the layer to the stage -stage.add(layer); - -// draw the image -layer.draw(); - -const steps = -[ - // Initialize phonebook - {mainHighlight: [1], funcHighlight: [], action: null, console: "Initializing phonebook array with size 10"}, - - // Add dan - {mainHighlight: [2], funcHighlight: [], action: null, console: "Adding entry: dan (5182764321)"}, - {mainHighlight: [2], funcHighlight: [13], action: null, console: "Entering add function"}, - {mainHighlight: [2], funcHighlight: [14], action: null, console: "Calculating hash index..."}, - {mainHighlight: [2], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [2], funcHighlight: [11], action: null, console: "5182764321 % 10 = 1"}, - {mainHighlight: [2], funcHighlight: [14], action: null, console: "Hash index: 1"}, - {mainHighlight: [2], funcHighlight: [15], action: null, console: "Creating new node"}, - {mainHighlight: [2], funcHighlight: [16], action: null, console: "Setting name: dan"}, - {mainHighlight: [2], funcHighlight: [17], action: null, console: "Setting number: 5182764321"}, - {mainHighlight: [2], funcHighlight: [18], action: null, console: "Setting next pointer"}, - {mainHighlight: [2], funcHighlight: [19], action: {type: 'add', index: 1, name: 'dan', number: '5182764321'}, console: "Adding node to index 1"}, - {mainHighlight: [2], funcHighlight: [20], action: null, console: "Returning from add function"}, - - // Add fred - {mainHighlight: [3], funcHighlight: [], action: null, console: "Adding entry: fred (6173551212)"}, - {mainHighlight: [3], funcHighlight: [13], action: null, console: "Entering add function"}, - {mainHighlight: [3], funcHighlight: [14], action: null, console: "Calculating hash index..."}, - {mainHighlight: [3], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [3], funcHighlight: [11], action: null, console: "6173551212 % 10 = 2"}, - {mainHighlight: [3], funcHighlight: [14], action: null, console: "Hash index: 2"}, - {mainHighlight: [3], funcHighlight: [15], action: null, console: "Creating new node"}, - {mainHighlight: [3], funcHighlight: [16], action: null, console: "Setting name: fred"}, - {mainHighlight: [3], funcHighlight: [17], action: null, console: "Setting number: 6173551212"}, - {mainHighlight: [3], funcHighlight: [18], action: null, console: "Setting next pointer"}, - {mainHighlight: [3], funcHighlight: [19], action: {type: 'add', index: 2, name: 'fred', number: '6173551212'}, console: "Adding node to index 2"}, - {mainHighlight: [3], funcHighlight: [20], action: null, console: "Returning from add function"}, - - // Add alice - {mainHighlight: [4], funcHighlight: [], action: null, console: "Adding entry: alice (5182761234)"}, - {mainHighlight: [4], funcHighlight: [13], action: null, console: "Entering add function"}, - {mainHighlight: [4], funcHighlight: [14], action: null, console: "Calculating hash index..."}, - {mainHighlight: [4], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [4], funcHighlight: [11], action: null, console: "5182761234 % 10 = 4"}, - {mainHighlight: [4], funcHighlight: [14], action: null, console: "Hash index: 4"}, - {mainHighlight: [4], funcHighlight: [15], action: null, console: "Creating new node"}, - {mainHighlight: [4], funcHighlight: [16], action: null, console: "Setting name: alice"}, - {mainHighlight: [4], funcHighlight: [17], action: null, console: "Setting number: 5182761234"}, - {mainHighlight: [4], funcHighlight: [18], action: null, console: "Setting next pointer"}, - {mainHighlight: [4], funcHighlight: [19], action: {type: 'add', index: 4, name: 'alice', number: '5182761234'}, console: "Adding node to index 4"}, - {mainHighlight: [4], funcHighlight: [20], action: null, console: "Returning from add function"}, - - // Add carol - {mainHighlight: [5], funcHighlight: [], action: null, console: "Adding entry: carol (5182761267)"}, - {mainHighlight: [5], funcHighlight: [13], action: null, console: "Entering add function"}, - {mainHighlight: [5], funcHighlight: [14], action: null, console: "Calculating hash index..."}, - {mainHighlight: [5], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [5], funcHighlight: [11], action: null, console: "5182761267 % 10 = 7"}, - {mainHighlight: [5], funcHighlight: [14], action: null, console: "Hash index: 7"}, - {mainHighlight: [5], funcHighlight: [15], action: null, console: "Creating new node"}, - {mainHighlight: [5], funcHighlight: [16], action: null, console: "Setting name: carol"}, - {mainHighlight: [5], funcHighlight: [17], action: null, console: "Setting number: 5182761267"}, - {mainHighlight: [5], funcHighlight: [18], action: null, console: "Setting next pointer"}, - {mainHighlight: [5], funcHighlight: [19], action: {type: 'add', index: 7, name: 'carol', number: '5182761267'}, console: "Adding node to index 7"}, - {mainHighlight: [5], funcHighlight: [20], action: null, console: "Returning from add function"}, - - // Add bob - {mainHighlight: [6], funcHighlight: [], action: null, console: "Adding entry: bob (5182765678)"}, - {mainHighlight: [6], funcHighlight: [13], action: null, console: "Entering add function"}, - {mainHighlight: [6], funcHighlight: [14], action: null, console: "Calculating hash index..."}, - {mainHighlight: [6], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [6], funcHighlight: [11], action: null, console: "5182765678 % 10 = 8"}, - {mainHighlight: [6], funcHighlight: [14], action: null, console: "Hash index: 8"}, - {mainHighlight: [6], funcHighlight: [15], action: null, console: "Creating new node"}, - {mainHighlight: [6], funcHighlight: [16], action: null, console: "Setting name: bob"}, - {mainHighlight: [6], funcHighlight: [17], action: null, console: "Setting number: 5182765678"}, - {mainHighlight: [6], funcHighlight: [18], action: null, console: "Setting next pointer"}, - {mainHighlight: [6], funcHighlight: [19], action: {type: 'add', index: 8, name: 'bob', number: '5182765678'}, console: "Adding node to index 8"}, - {mainHighlight: [6], funcHighlight: [20], action: null, console: "Returning from add function"}, - - // Add erin - {mainHighlight: [7], funcHighlight: [], action: null, console: "Adding entry: erin (5182764488)"}, - {mainHighlight: [7], funcHighlight: [13], action: null, console: "Entering add function"}, - {mainHighlight: [7], funcHighlight: [14], action: null, console: "Calculating hash index..."}, - {mainHighlight: [7], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [7], funcHighlight: [11], action: null, console: "5182764488 % 10 = 8"}, - {mainHighlight: [7], funcHighlight: [14], action: null, console: "Hash index: 8"}, - {mainHighlight: [7], funcHighlight: [15], action: null, console: "Creating new node"}, - {mainHighlight: [7], funcHighlight: [16], action: null, console: "Setting name: erin"}, - {mainHighlight: [7], funcHighlight: [17], action: null, console: "Setting number: 5182764488"}, - {mainHighlight: [7], funcHighlight: [18], action: null, console: "Setting next pointer"}, - {mainHighlight: [7], funcHighlight: [19], action: {type: 'add', index: 8, name: 'erin', number: '5182764488', chain: true}, console: "Adding node to index 8 (chaining)"}, - {mainHighlight: [7], funcHighlight: [20], action: null, console: "Returning from add function"}, - - // Identify dan - {mainHighlight: [8], funcHighlight: [], action: null, console: "Searching for number: 5182764321"}, - {mainHighlight: [8], funcHighlight: [21], action: null, console: "Entering identify function"}, - {mainHighlight: [8], funcHighlight: [22], action: null, console: "Calculating hash index..."}, - {mainHighlight: [8], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [8], funcHighlight: [11], action: null, console: "5182764321 % 10 = 1"}, - {mainHighlight: [8], funcHighlight: [22], action: null, console: "Hash index: 1"}, - {mainHighlight: [8], funcHighlight: [23], action: null, console: "Starting search at index 1"}, - {mainHighlight: [8], funcHighlight: [24], action: null, console: "phonebook[1] is not nullptr(true)"}, - {mainHighlight: [8], funcHighlight: [25], action: null, console: "5182764321 == 5182764321(true)"}, - {mainHighlight: [8], funcHighlight: [26], action: null, console: "Returning: dan"}, - {mainHighlight: [8], funcHighlight: [31], action: null, console: "Returning from identify function"}, - {mainHighlight: [8], funcHighlight: [], action: null, console: "Identify 5182764321: dan"}, - - // Identify erin - {mainHighlight: [9], funcHighlight: [], action: null, console: "Searching for number: 5182764488"}, - {mainHighlight: [9], funcHighlight: [21], action: null, console: "Entering identify function"}, - {mainHighlight: [9], funcHighlight: [22], action: null, console: "Calculating hash index..."}, - {mainHighlight: [9], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [9], funcHighlight: [11], action: null, console: "5182764488 % 10 = 8"}, - {mainHighlight: [9], funcHighlight: [22], action: null, console: "Hash index: 8"}, - {mainHighlight: [9], funcHighlight: [23], action: null, console: "Starting search at index 8"}, - {mainHighlight: [9], funcHighlight: [24], action: null, console: "phonebook[8] is not nullptr(true)"}, - {mainHighlight: [9], funcHighlight: [25], action: null, console: "5182765678 != 5182764488(false)"}, - {mainHighlight: [9], funcHighlight: [28], action: null, console: "Moving to next node"}, - {mainHighlight: [9], funcHighlight: [24], action: null, console: "phonebook[8]->next is not nullptr(true)"}, - {mainHighlight: [9], funcHighlight: [25], action: null, console: "5182764488 == 5182764488(true)"}, - {mainHighlight: [9], funcHighlight: [26], action: null, console: "Returning: erin"}, - {mainHighlight: [9], funcHighlight: [31], action: null, console: "Returning from identify function"}, - {mainHighlight: [9], funcHighlight: [], action: null, console: "Identify 5182764488: erin"}, - - // Identify fred - {mainHighlight: [10], funcHighlight: [], action: null, console: "Searching for number: 6173551212"}, - {mainHighlight: [10], funcHighlight: [21], action: null, console: "Entering identify function"}, - {mainHighlight: [10], funcHighlight: [22], action: null, console: "Calculating hash index..."}, - {mainHighlight: [10], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [10], funcHighlight: [11], action: null, console: "6173551212 % 10 = 2"}, - {mainHighlight: [10], funcHighlight: [22], action: null, console: "Hash index: 2"}, - {mainHighlight: [10], funcHighlight: [23], action: null, console: "Starting search at index 2"}, - {mainHighlight: [10], funcHighlight: [24], action: null, console: "phonebook[2] is not nullptr(true)"}, - {mainHighlight: [10], funcHighlight: [25], action: null, console: "6173551212 == 6173551212(true)"}, - {mainHighlight: [10], funcHighlight: [26], action: null, console: "Returning: fred"}, - {mainHighlight: [10], funcHighlight: [31], action: null, console: "Returning from identify function"}, - {mainHighlight: [10], funcHighlight: [], action: null, console: "Identify 6173551212: fred"}, - - // Identify not found - {mainHighlight: [11], funcHighlight: [], action: null, console: "Searching for number: 1234567890"}, - {mainHighlight: [11], funcHighlight: [21], action: null, console: "Entering identify function"}, - {mainHighlight: [11], funcHighlight: [22], action: null, console: "Calculating hash index..."}, - {mainHighlight: [11], funcHighlight: [10], action: null, console: "Entering hash_function"}, - {mainHighlight: [11], funcHighlight: [11], action: null, console: "1234567890 % 10 = 0"}, - {mainHighlight: [11], funcHighlight: [22], action: null, console: "Hash index: 0"}, - {mainHighlight: [11], funcHighlight: [23], action: null, console: "Starting search at index 0"}, - {mainHighlight: [11], funcHighlight: [24], action: null, console: "phonebook[0] is not nullptr(false)"}, - {mainHighlight: [11], funcHighlight: [30], action: null, console: "Returning: Not found"}, - {mainHighlight: [11], funcHighlight: [31], action: null, console: "Returning from identify function"}, - {mainHighlight: [11], funcHighlight: [], action: null, console: "Identify 1234567890: Not found"}, - - // Return 0 - {mainHighlight: [12], funcHighlight: [], action: null, console: "Program completed"} -]; - -function highlightCodeLines(mainLines, funcLines) -{ - for (let i = 0; i < mainCodeTextNodes.length; ++i) - { - if (mainLines.includes(i)) - { - mainCodeTextNodes[i].fontStyle('normal'); - mainCodeTextNodes[i].fill('#FF0000'); // Bright red for current line - } - else - { - mainCodeTextNodes[i].fontStyle('normal'); - mainCodeTextNodes[i].fill('#222'); - } - } - - for (let i = 0; i < funcCodeTextNodes.length; ++i) - { - if (funcLines.includes(i)) - { - funcCodeTextNodes[i].fontStyle('normal'); - funcCodeTextNodes[i].fill('#FF0000'); // Bright red for current line - } - else - { - funcCodeTextNodes[i].fontStyle('normal'); - funcCodeTextNodes[i].fill('#222'); - } - } - - layer.draw(); -} - -let stepIndex = 0; - -function nextstep() -{ - if (stepIndex >= steps.length) - { - alert('End of animation! Refresh to restart.'); - return; - } - - let step = steps[stepIndex]; - highlightCodeLines(step.mainHighlight, step.funcHighlight); - - // Perform the action first - if (step.action && step.action.type === 'add') - { - addNodeToMemory(step.action.index, step.action.name, step.action.number, step.action.chain); - // Then show the console message - if (step.console) - { - addConsoleText(step.console); - } - - layer.draw(); - ++stepIndex; - - } - else if (step.action && step.action.type === 'identify') - { - highlightIdentifyPath(step.action.index, step.action.path); - } - else - { - // No action, just show console message - if (step.console) - { - addConsoleText(step.console); - } - - layer.draw(); - ++stepIndex; - } -} - -window.nextstep = nextstep; - -function highlightNext(index, path, i) -{ - if (i < path.length) - { - let obj = memoryNodes[index][path[i]]; - - if (obj) - { - obj.group.children.each(child => { - if (child instanceof Konva.Rect) - { - child.stroke('#FF0000'); // red for current - child.strokeWidth(5); - } - }); - } - - layer.draw(); - ++i; - - if (i < path.length) - { - setTimeout(() => highlightNext(index, path, i), 1000); - } - } -} - -function highlightIdentifyPath(index, path) -{ - // Remove previous highlights - for (let idx in memoryNodes) - { - memoryNodes[idx].forEach(obj => { - obj.group.children.each(child => { - if (child instanceof Konva.Rect) - { - child.stroke('#222'); - child.strokeWidth(2); - } - }); - }); - } - - if (!memoryNodes[index]) return; - - // Start the animation - highlightNext(index, path, 0); -} - -// --- Main Code Box (left) --- -const mainCodeLines = -[ - 'int main(){', - ' Node *phonebook[PHONEBOOK_SIZE] = {nullptr};', - ' add(phonebook, 5182764321, "dan");', - ' add(phonebook, 6173551212, "fred");', - ' add(phonebook, 5182761234, "alice");', - ' add(phonebook, 5182761267, "carol");', - ' add(phonebook, 5182765678, "bob");', - ' add(phonebook, 5182764488, "erin");', - ' std::cout << "Identify 5182764321: " << identify(phonebook, 5182764321) << std::endl;', - ' std::cout << "Identify 5182764488: " << identify(phonebook, 5182764488) << std::endl;', - ' std::cout << "Identify 6173551212: " << identify(phonebook, 6173551212) << std::endl;', - ' std::cout << "Identify 1234567890: " << identify(phonebook, 1234567890) << std::endl;', - ' return 0;', - '}' -]; - -// --- Function Code Box (center) --- -const funcCodeLines = -[ - '#include ', - '#include ', - 'const int PHONEBOOK_SIZE = 10;', - 'struct Node{', - 'public:', - ' int number;', - ' std::string name;', - ' Node *next;', - ' Node() : name(""), number(0), next(nullptr) {}', - '};', - 'int hash_function(int number){', - ' return number % PHONEBOOK_SIZE;', - '}', - 'void add(Node *phonebook[PHONEBOOK_SIZE], int number, const std::string &name){', - ' int index = hash_function(number);', - ' Node *tmp = new Node;', - ' tmp->name = name;', - ' tmp->number = number;', - ' tmp->next = phonebook[index];', - ' phonebook[index] = tmp;', - '}', - 'std::string identify(Node *phonebook[PHONEBOOK_SIZE], int number){', - ' int index = hash_function(number);', - ' Node *curr = phonebook[index];', - ' while (curr != nullptr){', - ' if (curr->number == number){', - ' return curr->name;', - ' }', - ' curr = curr->next;', - ' }', - ' return "Not found";', - '}' -]; - -// Draw main code box (left) -var mainCodeBox = new Konva.Rect -({ - x: 20, - y: 25, - width: 800, - height: mainCodeLines.length * 24 + 40, - fill: '#ddd', - stroke: '#555', - strokeWidth: 5, - shadowColor: 'black', - shadowBlur: 10, - shadowOffsetX: 10, - shadowOffsetY: 10, - shadowOpacity: 0.2, - cornerRadius: 10, -}); - -layer.add(mainCodeBox); - -// Draw console box under main code box -var consoleBox = new Konva.Rect -({ - x: 20, - y: mainCodeBox.y() + mainCodeBox.height() + 40, - width: 800, - height: 100, - fill: '#ddd', - stroke: '#555', - strokeWidth: 5, - shadowColor: 'black', - shadowBlur: 10, - shadowOffsetX: 10, - shadowOffsetY: 10, - shadowOpacity: 0.2, - cornerRadius: 10, -}); - -layer.add(consoleBox); - -var consoleLabel = new Konva.Text -({ - x: 375, - y: consoleBox.y() - 25, - id: 'console', - text: 'Console', - fontSize: 28, - fontFamily: 'Calibri', - fill: '#000000', -}); - -layer.add(consoleLabel); - -let consoleTextNode = null; - -function addConsoleText(text) -{ - if (consoleTextNode) - { - consoleTextNode.destroy(); - } - - consoleTextNode = new Konva.Text - ({ - x: 30, - y: consoleBox.y() + 30, - text: text, - id: 'console_line', - fontSize: 16, - fontFamily: 'Consolas', - fill: '#000000', - width: 785, - padding: 1, - wrap: 'none', - }); - - layer.add(consoleTextNode); - layer.draw(); -} - -let mainCodeTextNodes = []; - -for (let i = 0; i < mainCodeLines.length; ++i) -{ - let t = new Konva.Text - ({ - x: 30, - y: 40 + i * 24, - text: mainCodeLines[i], - id: 'main_code_line_' + i, - fontSize: 16, - fontFamily: 'Consolas', - fill: '#222', - width: 785, - padding: 1, - wrap: 'none', - }); - - mainCodeTextNodes.push(t); - layer.add(t); -} - -// Function code box (center) -var funcCodeBox = new Konva.Rect -({ - x: 850, - y: 25, - width: 750, - height: funcCodeLines.length * 24 + 30, - fill: '#ddd', - stroke: '#555', - strokeWidth: 5, - shadowColor: 'black', - shadowBlur: 10, - shadowOffsetX: 10, - shadowOffsetY: 10, - shadowOpacity: 0.2, - cornerRadius: 10, -}); - -layer.add(funcCodeBox); - -var funcCodeLabel = new Konva.Text -({ - x: 1175, - y: 0, - id: 'func_code', - text: 'Functions', - fontSize: 28, - fontFamily: 'Calibri', - fill: '#000000', -}); - -layer.add(funcCodeLabel); - -let funcCodeTextNodes = []; - -for (let i = 0; i < funcCodeLines.length; ++i) -{ - let t = new Konva.Text - ({ - x: 875, - y: 40 + i * 24, - text: funcCodeLines[i], - id: 'func_code_line_' + i, - fontSize: 16, - fontFamily: 'Consolas', - fill: '#222', - width: 750, - padding: 1, - wrap: 'none', - }); - - funcCodeTextNodes.push(t); - layer.add(t); -} - -let memoryNodes = {}; - -function addNodeToMemory(index, name, number, chain = false) -{ - if (!memoryNodes[index]) memoryNodes[index] = []; - let y = 60 + index * 50 + 10; - let x = 1725 + memoryNodes[index].length * 150; - - // Draw node group (rectangle + text) - let group = new Konva.Group(); - - let nodeRect = new Konva.Rect - ({ - x: x, - y: y, - width: 100, - height: 30, - fill: '#fff', - stroke: '#222', - strokeWidth: 2, - cornerRadius: 5, - }); - - let numberText = new Konva.Text - ({ - x: x + 5, - y: y + 2, - text: number, - fontSize: 12, - fontFamily: 'Consolas', - fill: '#333', - }); - - let nameText = new Konva.Text - ({ - x: x + 5, - y: y + 15, - text: name, - fontSize: 15, - fontFamily: 'Calibri', - fill: '#000', - fontStyle: 'bold', - }); - - group.add(nodeRect); - group.add(numberText); - group.add(nameText); - layer.add(group); - - // Draw arrow from memory box or previous node - let arrow; - - if (memoryNodes[index].length === 0) - { - arrow = new Konva.Arrow - ({ - points: [1700, y + 15, x, y + 15], - pointerLength: 10, - pointerWidth: 10, - fill: 'black', - stroke: 'black', - strokeWidth: 2, - }); - } - else - { - let prevX = 1725 + (memoryNodes[index].length - 1) * 150 + 100; - - arrow = new Konva.Arrow - ({ - points: [prevX, y + 15, x, y + 15], - pointerLength: 10, - pointerWidth: 10, - fill: 'black', - stroke: 'black', - strokeWidth: 2, - }); - } - - layer.add(arrow); - memoryNodes[index].push({group, arrow}); -} +// Author: Xi Chen +// first we need to create a stage +var stage = new Konva.Stage({ + container: 'container', // id of container
+ width: 2000, + height: 1000 +}); + +// then create layer +var layer = new Konva.Layer(); + +function makeMemory(xstart, ystart, bufferSize, w, h) +{ + for (let i = 0; i < bufferSize; ++i) + { + let tr = new Konva.Rect + ({ + x: xstart, + y: ystart+i*h, + id:"brec"+i, + stroke: '#343434', + strokeWidth: 5, + fill: '#ddd', + width: w, + height: h, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + }); + + layer.add(tr); + + // Add index number to the left of each box + let indexText = new Konva.Text + ({ + x: xstart - 30, // Position to the left of the box + y: ystart + i*h + h/2 - 10, // Center vertically + text: i.toString(), + fontSize: 18, + fontFamily: 'Calibri', + fill: '#000000', + align: 'center', + width: 30, + }); + + layer.add(indexText); + } // end of for loop +} + +makeMemory(1650,60,10,50,50); + +var memoryText = new Konva.Text +({ + x: 1625, + y: 25, + id:"memory", + text: "Memory", + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +var mainLabel = new Konva.Text +({ + x: 400, + y: 0, + id:"main", + text: "Main", + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +// box for code +layer.add(memoryText); +layer.add(mainLabel); + +// add the layer to the stage +stage.add(layer); + +// draw the image +layer.draw(); + +const steps = +[ + // Initialize phonebook + {mainHighlight: [1], funcHighlight: [], action: null, console: "Initializing phonebook array with size 10"}, + + // Add dan + {mainHighlight: [2], funcHighlight: [], action: null, console: "Adding entry: dan (5182764321)"}, + {mainHighlight: [2], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [2], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [2], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [2], funcHighlight: [11], action: null, console: "5182764321 % 10 = 1"}, + {mainHighlight: [2], funcHighlight: [14], action: null, console: "Hash index: 1"}, + {mainHighlight: [2], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [2], funcHighlight: [16], action: null, console: "Setting name: dan"}, + {mainHighlight: [2], funcHighlight: [17], action: null, console: "Setting number: 5182764321"}, + {mainHighlight: [2], funcHighlight: [18], action: {type: 'highlight_memory', index: 1, color: '#00FF00'}, console: "Setting next pointer"}, + {mainHighlight: [2], funcHighlight: [19], action: {type: 'add', index: 1, name: 'dan', number: '5182764321', color: '#0000FF'}, console: "Adding node to index 1"}, + {mainHighlight: [2], funcHighlight: [20], action: {type: 'highlight_memory', index: 1, color: '#343434'}, console: "Returning from add function"}, + {mainHighlight: [2], funcHighlight: [20], action: {type: 'highlight_person', index: 1, name: 'dan', number: '5182764321', color: '#222'}, console: "Returning from add function"}, + + // Add fred + {mainHighlight: [3], funcHighlight: [], action: null, console: "Adding entry: fred (6173551212)"}, + {mainHighlight: [3], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [3], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [3], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [3], funcHighlight: [11], action: null, console: "6173551212 % 10 = 2"}, + {mainHighlight: [3], funcHighlight: [14], action: null, console: "Hash index: 2"}, + {mainHighlight: [3], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [3], funcHighlight: [16], action: null, console: "Setting name: fred"}, + {mainHighlight: [3], funcHighlight: [17], action: null, console: "Setting number: 6173551212"}, + {mainHighlight: [3], funcHighlight: [18], action: {type: 'highlight_memory', index: 2, color: '#00FF00'}, console: "Setting next pointer"}, + {mainHighlight: [3], funcHighlight: [19], action: {type: 'add', index: 2, name: 'fred', number: '6173551212', color: '#0000FF'}, console: "Adding node to index 2"}, + {mainHighlight: [3], funcHighlight: [20], action: {type: 'highlight_memory', index: 2, color: '#343434'}, console: "Returning from add function"}, + {mainHighlight: [3], funcHighlight: [20], action: {type: 'highlight_person', index: 2, name: 'fred', number: '6173551212', color: '#222'}, console: "Returning from add function"}, + + // Add alice + {mainHighlight: [4], funcHighlight: [], action: null, console: "Adding entry: alice (5182761234)"}, + {mainHighlight: [4], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [4], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [4], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [4], funcHighlight: [11], action: null, console: "5182761234 % 10 = 4"}, + {mainHighlight: [4], funcHighlight: [14], action: null, console: "Hash index: 4"}, + {mainHighlight: [4], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [4], funcHighlight: [16], action: null, console: "Setting name: alice"}, + {mainHighlight: [4], funcHighlight: [17], action: null, console: "Setting number: 5182761234"}, + {mainHighlight: [4], funcHighlight: [18], action: {type: 'highlight_memory', index: 4, color: '#00FF00'}, console: "Setting next pointer"}, + {mainHighlight: [4], funcHighlight: [19], action: {type: 'add', index: 4, name: 'alice', number: '5182761234', color: '#0000FF'}, console: "Adding node to index 4"}, + {mainHighlight: [4], funcHighlight: [20], action: {type: 'highlight_memory', index: 4, color: '#343434'}, console: "Returning from add function"}, + {mainHighlight: [4], funcHighlight: [20], action: {type: 'highlight_person', index: 4, name: 'alice', number: '5182761234', color: '#222'}, console: "Returning from add function"}, + + // Add carol + {mainHighlight: [5], funcHighlight: [], action: null, console: "Adding entry: carol (5182761267)"}, + {mainHighlight: [5], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [5], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [5], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [5], funcHighlight: [11], action: null, console: "5182761267 % 10 = 7"}, + {mainHighlight: [5], funcHighlight: [14], action: null, console: "Hash index: 7"}, + {mainHighlight: [5], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [5], funcHighlight: [16], action: null, console: "Setting name: carol"}, + {mainHighlight: [5], funcHighlight: [17], action: null, console: "Setting number: 5182761267"}, + {mainHighlight: [5], funcHighlight: [18], action: {type: 'highlight_memory', index: 7, color: '#00FF00'}, console: "Setting next pointer"}, + {mainHighlight: [5], funcHighlight: [19], action: {type: 'add', index: 7, name: 'carol', number: '5182761267', color: '#0000FF'}, console: "Adding node to index 7"}, + {mainHighlight: [5], funcHighlight: [20], action: {type: 'highlight_memory', index: 7, color: '#343434'}, console: "Returning from add function"}, + {mainHighlight: [5], funcHighlight: [20], action: {type: 'highlight_person', index: 7, name: 'carol', number: '5182761267', color: '#222'}, console: "Returning from add function"}, + + // Add bob + {mainHighlight: [6], funcHighlight: [], action: null, console: "Adding entry: bob (5182765678)"}, + {mainHighlight: [6], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [6], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [6], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [6], funcHighlight: [11], action: null, console: "5182765678 % 10 = 8"}, + {mainHighlight: [6], funcHighlight: [14], action: null, console: "Hash index: 8"}, + {mainHighlight: [6], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [6], funcHighlight: [16], action: null, console: "Setting name: bob"}, + {mainHighlight: [6], funcHighlight: [17], action: null, console: "Setting number: 5182765678"}, + {mainHighlight: [6], funcHighlight: [18], action: {type: 'highlight_memory', index: 8, color: '#00FF00'}, console: "Setting next pointer"}, + {mainHighlight: [6], funcHighlight: [19], action: {type: 'add', index: 8, name: 'bob', number: '5182765678', color: '#0000FF'}, console: "Adding node to index 8"}, + {mainHighlight: [6], funcHighlight: [20], action: {type: 'highlight_memory', index: 8, color: '#343434'}, console: "Returning from add function"}, + {mainHighlight: [6], funcHighlight: [20], action: {type: 'highlight_person', index: 8, name: 'bob', number: '5182765678', color: '#222'}, console: "Returning from add function"}, + + // Add erin + {mainHighlight: [7], funcHighlight: [], action: null, console: "Adding entry: erin (5182764488)"}, + {mainHighlight: [7], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [7], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [7], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [7], funcHighlight: [11], action: null, console: "5182764488 % 10 = 8"}, + {mainHighlight: [7], funcHighlight: [14], action: null, console: "Hash index: 8"}, + {mainHighlight: [7], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [7], funcHighlight: [16], action: null, console: "Setting name: erin"}, + {mainHighlight: [7], funcHighlight: [17], action: null, console: "Setting number: 5182764488"}, + {mainHighlight: [7], funcHighlight: [18], action: {type: 'highlight_memory', index: 8, color: '#00FF00'}, console: "Setting next pointer"}, + {mainHighlight: [7], funcHighlight: [19], action: {type: 'add', index: 8, name: 'erin', number: '5182764488', chain: true, color: '#0000FF'}, console: "Adding node to index 8 (chaining)"}, + {mainHighlight: [7], funcHighlight: [20], action: {type: 'highlight_memory', index: 8, color: '#343434'}, console: "Returning from add function"}, + {mainHighlight: [7], funcHighlight: [20], action: {type: 'highlight_person', index: 8, name: 'erin', number: '5182764488', color: '#222'}, console: "Returning from add function"}, + + // Identify dan + {mainHighlight: [8], funcHighlight: [], action: null, console: "Searching for number: 5182764321"}, + {mainHighlight: [8], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [8], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [8], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [8], funcHighlight: [11], action: null, console: "5182764321 % 10 = 1"}, + {mainHighlight: [8], funcHighlight: [22], action: null, console: "Hash index: 1"}, + {mainHighlight: [8], funcHighlight: [23], action: {type: 'highlight_memory', index: 1, color: '#00FF00'}, console: "Starting search at index 1"}, + {mainHighlight: [8], funcHighlight: [24], action: null, console: "phonebook[1] is not nullptr(true)"}, + {mainHighlight: [8], funcHighlight: [25], action: null, console: "5182764321 == 5182764321(true)"}, + {mainHighlight: [8], funcHighlight: [26], action: {type: 'idan', index: 1, name: 'dan', color: '#0000FF'}, console: "Returning: dan"}, + {mainHighlight: [8], funcHighlight: [31], action: {type: 'idan', index: 1, name: 'dan', color: '#222'}, console: "Returning from identify function"}, + {mainHighlight: [8], funcHighlight: [], action: {type: 'highlight_memory', index: 1, color: '#343434'}, console: "Identify 5182764321: dan"}, + + // Identify erin + {mainHighlight: [9], funcHighlight: [], action: null, console: "Searching for number: 5182764488"}, + {mainHighlight: [9], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [9], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [9], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [9], funcHighlight: [11], action: null, console: "5182764488 % 10 = 8"}, + {mainHighlight: [9], funcHighlight: [22], action: null, console: "Hash index: 8"}, + {mainHighlight: [9], funcHighlight: [23], action: {type: 'highlight_memory', index: 8, color: '#00FF00'}, console: "Starting search at index 8"}, + {mainHighlight: [9], funcHighlight: [24], action: null, console: "phonebook[8] is not nullptr(true)"}, + {mainHighlight: [9], funcHighlight: [25], action: null, console: "5182765678 != 5182764488(false)"}, + {mainHighlight: [9], funcHighlight: [28], action: null, console: "Moving to next node"}, + {mainHighlight: [9], funcHighlight: [24], action: null, console: "phonebook[8]->next is not nullptr(true)"}, + {mainHighlight: [9], funcHighlight: [25], action: null, console: "5182764488 == 5182764488(true)"}, + {mainHighlight: [9], funcHighlight: [26], action: {type: 'ierin', index: 8, name: 'erin', color: '#0000FF'}, console: "Returning: erin"}, + {mainHighlight: [9], funcHighlight: [31], action: {type: 'ierin', index: 8, name: 'erin', color: '#222'}, console: "Returning from identify function"}, + {mainHighlight: [9], funcHighlight: [], action: {type: 'highlight_memory', index: 8, color: '#343434'}, console: "Identify 5182764488: erin"}, + + // Identify fred + {mainHighlight: [10], funcHighlight: [], action: null, console: "Searching for number: 6173551212"}, + {mainHighlight: [10], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [10], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [10], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [10], funcHighlight: [11], action: null, console: "6173551212 % 10 = 2"}, + {mainHighlight: [10], funcHighlight: [22], action: null, console: "Hash index: 2"}, + {mainHighlight: [10], funcHighlight: [23], action: {type: 'highlight_memory', index: 2, color: '#00FF00'}, console: "Starting search at index 2"}, + {mainHighlight: [10], funcHighlight: [24], action: null, console: "phonebook[2] is not nullptr(true)"}, + {mainHighlight: [10], funcHighlight: [25], action: null, console: "6173551212 == 6173551212(true)"}, + {mainHighlight: [10], funcHighlight: [26], action: {type: 'ifred', index: 2, name: 'fred', color: '#0000FF'}, console: "Returning: fred"}, + {mainHighlight: [10], funcHighlight: [31], action: {type: 'ifred', index: 2, name: 'fred', color: '#222'}, console: "Returning from identify function"}, + {mainHighlight: [10], funcHighlight: [], action: {type: 'highlight_memory', index: 2, color: '#343434'}, console: "Identify 6173551212: fred"}, + + // Identify not found + {mainHighlight: [11], funcHighlight: [], action: null, console: "Searching for number: 1234567890"}, + {mainHighlight: [11], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [11], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [11], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [11], funcHighlight: [11], action: null, console: "1234567890 % 10 = 0"}, + {mainHighlight: [11], funcHighlight: [22], action: null, console: "Hash index: 0"}, + {mainHighlight: [11], funcHighlight: [23], action: null, console: "Starting search at index 0"}, + {mainHighlight: [11], funcHighlight: [24], action: null, console: "phonebook[0] is not nullptr(false)"}, + {mainHighlight: [11], funcHighlight: [30], action: null, console: "Returning: Not found"}, + {mainHighlight: [11], funcHighlight: [31], action: null, console: "Returning from identify function"}, + {mainHighlight: [11], funcHighlight: [], action: null, console: "Identify 1234567890: Not found"}, + + // Return 0 + {mainHighlight: [12], funcHighlight: [], action: null, console: "Program completed"} +]; + +function highlightCodeLines(mainLines, funcLines) +{ + for (let i = 0; i < mainCodeTextNodes.length; ++i) + { + if (mainLines.includes(i)) + { + mainCodeTextNodes[i].fontStyle('normal'); + mainCodeTextNodes[i].fill('#FF0000'); // Bright red for current line + } + else + { + mainCodeTextNodes[i].fontStyle('normal'); + mainCodeTextNodes[i].fill('#222'); + } + } + + for (let i = 0; i < funcCodeTextNodes.length; ++i) + { + if (funcLines.includes(i)) + { + funcCodeTextNodes[i].fontStyle('normal'); + funcCodeTextNodes[i].fill('#FF0000'); // Bright red for current line + } + else + { + funcCodeTextNodes[i].fontStyle('normal'); + funcCodeTextNodes[i].fill('#222'); + } + } + + layer.draw(); +} + +let stepIndex = 0; + +function nextstep() +{ + if (stepIndex >= steps.length) + { + alert('End of animation! Refresh to restart.'); + return; + } + + let step = steps[stepIndex]; + highlightCodeLines(step.mainHighlight, step.funcHighlight); + + // Perform the action first + if (step.action) + { + if (step.action.type === 'add') + { + addNodeToMemory(step.action.index, step.action.name, step.action.number, step.action.chain, step.action.color); + } + else if (step.action.type === 'highlight_memory') + { + highlightMemoryBox(step.action.index, step.action.color); + } + else if (step.action.type === 'idan') + { + highlightPersonBox(step.action.index, step.action.name, 5182764321, step.action.color); + } + else if (step.action.type === 'ierin') + { + highlightPersonBox(step.action.index, step.action.name, 5182764488, step.action.color); + } + else if (step.action.type === 'ifred') + { + highlightPersonBox(step.action.index, step.action.name, 6173551212, step.action.color); + } + else if (step.action.type === 'highlight_person') + { + highlightPersonBox(step.action.index, step.action.name, step.action.number, step.action.color); + } + } + + // Then show the console message + if (step.console) + { + addConsoleText(step.console); + } + + layer.draw(); + ++stepIndex; +} + +window.nextstep = nextstep; + +function highlightNext(index, path, i) +{ + if (i < path.length) + { + let obj = memoryNodes[index][path[i]]; + + if (obj) + { + obj.group.children.each(child => { + if (child instanceof Konva.Rect) + { + child.stroke('#FF0000'); // red for current + child.strokeWidth(5); + } + }); + } + + layer.draw(); + ++i; + + if (i < path.length) + { + setTimeout(() => highlightNext(index, path, i), 1000); + } + } +} + +function highlightIdentifyPath(index, path) +{ + // Remove previous highlights + for (let idx in memoryNodes) + { + memoryNodes[idx].forEach(obj => { + obj.group.children.each(child => { + if (child instanceof Konva.Rect) + { + child.stroke('#222'); + child.strokeWidth(2); + } + }); + }); + } + + if (!memoryNodes[index]) return; + + // Start the animation + highlightNext(index, path, 0); +} + +// --- Main Code Box (left) --- +const mainCodeLines = +[ + 'int main(){', + ' Node *phonebook[PHONEBOOK_SIZE] = {nullptr};', + ' add(phonebook, 5182764321, "dan");', + ' add(phonebook, 6173551212, "fred");', + ' add(phonebook, 5182761234, "alice");', + ' add(phonebook, 5182761267, "carol");', + ' add(phonebook, 5182765678, "bob");', + ' add(phonebook, 5182764488, "erin");', + ' std::cout << "Identify 5182764321: " << identify(phonebook, 5182764321) << std::endl;', + ' std::cout << "Identify 5182764488: " << identify(phonebook, 5182764488) << std::endl;', + ' std::cout << "Identify 6173551212: " << identify(phonebook, 6173551212) << std::endl;', + ' std::cout << "Identify 1234567890: " << identify(phonebook, 1234567890) << std::endl;', + ' return 0;', + '}' +]; + +// --- Function Code Box (center) --- +const funcCodeLines = +[ + '#include ', + '#include ', + 'const int PHONEBOOK_SIZE = 10;', + 'struct Node{', + 'public:', + ' int number;', + ' std::string name;', + ' Node *next;', + ' Node() : name(""), number(0), next(nullptr) {}', + '};', + 'int hash_function(int number){', + ' return number % PHONEBOOK_SIZE;', + '}', + 'void add(Node *phonebook[PHONEBOOK_SIZE], int number, const std::string &name){', + ' int index = hash_function(number);', + ' Node *tmp = new Node;', + ' tmp->name = name;', + ' tmp->number = number;', + ' tmp->next = phonebook[index];', + ' phonebook[index] = tmp;', + '}', + 'std::string identify(Node *phonebook[PHONEBOOK_SIZE], int number){', + ' int index = hash_function(number);', + ' Node *curr = phonebook[index];', + ' while (curr != nullptr){', + ' if (curr->number == number){', + ' return curr->name;', + ' }', + ' curr = curr->next;', + ' }', + ' return "Not found";', + '}' +]; + +// Draw main code box (left) +var mainCodeBox = new Konva.Rect +({ + x: 20, + y: 25, + width: 800, + height: mainCodeLines.length * 24 + 40, + fill: '#ddd', + stroke: '#555', + strokeWidth: 5, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); + +layer.add(mainCodeBox); + +// Draw console box under main code box +var consoleBox = new Konva.Rect +({ + x: 20, + y: mainCodeBox.y() + mainCodeBox.height() + 40, + width: 800, + height: 100, + fill: '#ddd', + stroke: '#555', + strokeWidth: 5, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); + +layer.add(consoleBox); + +var consoleLabel = new Konva.Text +({ + x: 375, + y: consoleBox.y() - 25, + id: 'console', + text: 'Console', + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +layer.add(consoleLabel); + +let consoleTextNode = null; + +function addConsoleText(text) +{ + if (consoleTextNode) + { + consoleTextNode.destroy(); + } + + consoleTextNode = new Konva.Text + ({ + x: 30, + y: consoleBox.y() + 30, + text: text, + id: 'console_line', + fontSize: 16, + fontFamily: 'Consolas', + fill: '#000000', + width: 785, + padding: 1, + wrap: 'none', + }); + + layer.add(consoleTextNode); + layer.draw(); +} + +let mainCodeTextNodes = []; + +for (let i = 0; i < mainCodeLines.length; ++i) +{ + let t = new Konva.Text + ({ + x: 30, + y: 40 + i * 24, + text: mainCodeLines[i], + id: 'main_code_line_' + i, + fontSize: 16, + fontFamily: 'Consolas', + fill: '#222', + width: 785, + padding: 1, + wrap: 'none', + }); + + mainCodeTextNodes.push(t); + layer.add(t); +} + +// Function code box (center) +var funcCodeBox = new Konva.Rect +({ + x: 850, + y: 25, + width: 750, + height: funcCodeLines.length * 24 + 30, + fill: '#ddd', + stroke: '#555', + strokeWidth: 5, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); + +layer.add(funcCodeBox); + +var funcCodeLabel = new Konva.Text +({ + x: 1175, + y: 0, + id: 'func_code', + text: 'Functions', + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +layer.add(funcCodeLabel); + +let funcCodeTextNodes = []; + +for (let i = 0; i < funcCodeLines.length; ++i) +{ + let t = new Konva.Text + ({ + x: 875, + y: 40 + i * 24, + text: funcCodeLines[i], + id: 'func_code_line_' + i, + fontSize: 16, + fontFamily: 'Consolas', + fill: '#222', + width: 750, + padding: 1, + wrap: 'none', + }); + + funcCodeTextNodes.push(t); + layer.add(t); +} + +let memoryNodes = {}; + +function addNodeToMemory(index, name, number, chain = false, color = '#222') +{ + if (!memoryNodes[index]) memoryNodes[index] = []; + let y = 60 + index * 50 + 10; + let x = 1725 + memoryNodes[index].length * 150; + + // Draw node group (rectangle + text) + let group = new Konva.Group(); + + let nodeRect = new Konva.Rect + ({ + x: x, + y: y, + width: 100, + height: 30, + fill: '#fff', + stroke: color, + strokeWidth: 4, + cornerRadius: 5, + }); + + let numberText = new Konva.Text + ({ + x: x + 5, + y: y + 2, + text: String(number), + fontSize: 12, + fontFamily: 'Consolas', + fill: '#333', + }); + + let nameText = new Konva.Text + ({ + x: x + 5, + y: y + 15, + text: name, + fontSize: 15, + fontFamily: 'Calibri', + fill: '#000', + fontStyle: 'bold', + }); + + group.add(nodeRect); + group.add(numberText); + group.add(nameText); + layer.add(group); + + // Draw arrow from memory box or previous node + let arrow; + + if (memoryNodes[index].length === 0) + { + arrow = new Konva.Arrow + ({ + points: [1700, y + 15, x, y + 15], + pointerLength: 10, + pointerWidth: 10, + fill: 'black', + stroke: 'black', + strokeWidth: 2, + }); + } + else + { + let prevX = 1725 + (memoryNodes[index].length - 1) * 150 + 100; + + arrow = new Konva.Arrow + ({ + points: [prevX, y + 15, x, y + 15], + pointerLength: 10, + pointerWidth: 10, + fill: 'black', + stroke: 'black', + strokeWidth: 2, + }); + } + + layer.add(arrow); + memoryNodes[index].push({group, arrow}); +} + +function highlightMemoryBox(index, color) +{ + // Find the memory box by its ID + const memoryBox = layer.findOne('#brec' + index); + + if (memoryBox) + { + memoryBox.stroke(color); + memoryBox.strokeWidth(5); + layer.draw(); + } +} + +function highlightPersonBox(index, name, number, color) +{ + let y = 60 + index * 50 + 10; + let x = 1725 + (memoryNodes[index].length - 1) * 150; + + // Draw node group (rectangle + text) + let group = new Konva.Group(); + + let nodeRect = new Konva.Rect + ({ + x: x, + y: y, + width: 100, + height: 30, + fill: '#fff', + stroke: color, + strokeWidth: 4, + cornerRadius: 5, + }); + + let numberText = new Konva.Text + ({ + x: x + 5, + y: y + 2, + text: String(number), + fontSize: 12, + fontFamily: 'Consolas', + fill: '#333', + }); + + let nameText = new Konva.Text + ({ + x: x + 5, + y: y + 15, + text: name, + fontSize: 15, + fontFamily: 'Calibri', + fill: '#000', + fontStyle: 'bold', + }); + + group.add(nodeRect); + group.add(numberText); + group.add(nameText); + layer.add(group); + layer.draw(); +} \ No newline at end of file