// Change ITEMS for amount of bars. let ITEMS = 50; // Grab canvas and context. let canvas = document.getElementById('display'); let context = canvas.getContext('2d'); // Declare array to be ITEMS long. let list = null; let history = null; let showing = 0; let comparison_count = 0; let swap_count = 0; // adapted from https://en.wikipedia.org/wiki/Comb_sort let gap = ITEMS let shrink = 1.3 // Flags used to determine if bg or bars need redrawn. let updateBG = true; let updateBars = true; let paused = false; // Delay between comparasins. let sortDelay = 100; let sortDelay_interval = null; // Setup event listeners for window. function setupCanvas() { // Setup event listeners for load and window resize. window.addEventListener('load', canvasResize); window.addEventListener('resize', canvasResize); window.addEventListener('keydown', keydownEvent); } function keydownEvent(ev) { var k = ev.key; if (k == ' ') { paused = !paused; } else if (k == 'ArrowUp' && ! paused) { // up sortDelay /= 2; if (sortDelay < 1) sortDelay = 1 clearInterval(sortDelay_interval); sortDelay_interval = setInterval(sortLoop, sortDelay); } else if (k == 'ArrowDown' && ! paused) { // down sortDelay *= 2; if (sortDelay < 1) sortDelay = 1 clearInterval(sortDelay_interval); sortDelay_interval = setInterval(sortLoop, sortDelay); } else if (k == 'ArrowLeft') { if (showing > 0) showing--; } else if (k == 'ArrowRight') { if (showing+1 < history.length) showing++; } updateBG = true; updateBars = true; } // Initializes the list of 100 random numbers. function initList() { list = new Array(ITEMS); history = new Array(0); for (let i = 0; i < ITEMS; i++) { list[i] = Math.round(Math.random() * 100); } comparison_count = 0; swap_count = 0; gap = ITEMS history = new Array(0); history.push(list.slice(0)); showing = 0; } // This function grabs the width and height of the window // and resizes the canvas in the event of loading or resizing. function canvasResize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; // removed the following two lines to make printing text work properly (not inverted) //context.translate(0, canvas.height); // context.scale(1, -1); updateBG = true; updateBars = true; } // Finds the largest number in the list. function max() { let maximum = list[0]; for (let i = 1; i < ITEMS; i++) maximum = list[i] > maximum ? list[i] : maximum; return maximum; } // Comparing indicators to draw in different colors. // cmp1 and cmp2 are two indices being compared, make them // negative to mean a switch is happening let cmp1 = 0; let cmp2 = 0; // Plot each index of list[]. function plotBars() { var list = history[showing]; let length = list.length; let barWidth = canvas.width / length; let barScale = canvas.height / max(); for (let i = 0; i < ITEMS; i++) { let x = i * barWidth; let h = list[i] * barScale; let y = canvas.height - h; context.save(); if (i == cmp1 || i == cmp2) context.fillStyle = 'white'; else if (i == -cmp1 || i == -cmp2) context.fillStyle = 'yellow'; else context.fillStyle = 'orange'; context.fillRect(x+1, y, barWidth-2, h); context.restore(); } } // Draws the black background. function drawBackground() { // Save context to stack. context.save(); context.fillStyle = 'black'; context.fillRect(0, 0, canvas.width, canvas.height); // Pops most recently saved canvas state off stack. context.restore(); context.fillStyle = 'grey'; context.font = "30px Ariel"; context.fillText("Comb Sort, " + ITEMS + " numbers", 30, 25); context.fillStyle = 'white'; context.fillText('bars being compared - white', 30, 50); context.fillStyle = 'yellow'; context.fillText('bars being swapped - yellow', 30, 75); context.fillStyle = 'grey'; context.fillText('# comparisons so far - ' + comparison_count, 30, 100); context.fillText('# swaps so far - ' + swap_count, 30, 125); context.fillText('sortDelay - ' + sortDelay + 'ms', 30, 150); context.fillText('showing - ' + showing, 30, 175); context.fillText('space - pause, up/down - speed, left/right - history while paused', 400, 25); } // These are some variables that flag and alter various parts // of the sort. You may need to add/remove/modify your own // depending on the complexity of your sorting method. let sortStarted = false; let sortFinished = false; // Delay between sorting simulations. let restartDelay = 2000; let sortIndex = 0; function sortLoop() { if (! paused) { // If left > right, swap comparison_count = comparison_count + 1; cmp1 = sortIndex cmp2 = sortIndex + gap; if (cmp2 < ITEMS && list[cmp1] > list[cmp2]) { swap_count += 1; // Keep setting finished just to be safe. finished = false; // ol-switcharoo tmp = list[cmp2]; list[cmp2] = list[cmp1]; list[cmp1] = tmp; cmp1 *= -1; // flags that a swap is being done cmp2 *= -1; // flags that a swap is being done } // Flag that the background and bars need updating. updateBG = true; updateBars = true; history.push(list.slice(0)); showing = history.length - 1; sortIndex++; // Reached end of list. // If list has been scanned once with no swaps, // sortFinish will be set and sort will end. // Otherwise start over from 1. if (sortIndex + gap >= ITEMS) { if (finished) { sortFinished = true; clearInterval(sortLoop); } sortIndex = 0; gap = Math.floor(gap / shrink) if (gap <= 1) { gap = 1; finished = true; } } } // Repeat once every <sortDelay>. } // Sorting function. This example uses bubble sort. function sortStart() { // Finished is used to mark when no swaps were done in a sort loop. // Think of it as finishing one sorting loop. let finished = false; // note - set to false except for when gap is 1 // tmp is used as a temporary value when swapping. let tmp; // Flip the sortStarted flag. sortStarted = true; gap = Math.floor(gap / shrink) if (gap <= 1) { // for gap=1, keep track of if there were any swaps gap = 1 finished = true } // setInterval() runs once every <sortDelay> sortDelay_interval = setInterval(sortLoop, sortDelay); } // Setup initializes the canvas as well as the list of random nums. function setup() { setupCanvas(); initList(); } // loopBlock is briefly enabled when sorting has finished to give a view of the finished sort. let loopBlock = false; function loop() { if (!loopBlock) { // If background needs updated, redraw background. if (updateBG) { updateBG = false; drawBackground(); } // If bars need updated, replot. if (updateBars) { updateBars = false; plotBars(); } // Starts the sort if it isn't looping already. if (!sortStarted) sortStart(); // Checks if sort has completed. Briefly blocks looping while re-initializing the list, sortStarted, and sortFinished variables. if (sortFinished) { loopBlock = true; clearInterval(sortDelay_interval); sortDelay_interval = null; setTimeout(function() { initList(); sortStarted = false; sortFinished = false; loopBlock = false; }, restartDelay); } } } // Main function which runs the setup and holds the main infinite loop. (function main() { var x = window.prompt('How many items?', '50') ITEMS = x; // window.alert()... // Setup the canvas and list. setup(); (function mainLoop() { loop(); // requestAnimationFrame calls mainLoop infinitely requestAnimationFrame(mainLoop); })(); })();