// 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);
})();
})();