diff --git a/index.html b/index.html index 1d53c46..34eb5e1 100644 --- a/index.html +++ b/index.html @@ -98,7 +98,7 @@ } .controls-container { - height:50px; + height:45px; padding-bottom:10px; text-align:center; } @@ -135,7 +135,6 @@ .message-container { border-top:1px solid #ddd; - font-size:13px; height:100px; line-height:20px; text-align:right; @@ -143,23 +142,22 @@ .message-container .message { border-bottom:1px solid #ddd; - font-size:13px; + font-size:12px; } .range-container { height:50px; - margin-top:30px; + margin-top:40px; text-align:center; } .range-container input { - margin-bottom:10px; + margin-bottom:5px; } .range-container .msg { - font-size:13px; + font-size:11px; } -
diff --git a/js/quicksort.js b/js/quicksort.js index ff40814..a5c2a3e 100644 --- a/js/quicksort.js +++ b/js/quicksort.js @@ -1,28 +1,27 @@ /** * */ -var QuickSort = function() { - this.instructions = []; +var QuickSort = function(Visualizer) { this.swaps = 0; this.comparisons = 0; + this.V = Visualizer; + + this.V + .instruct(this.V.message, 0, 2, 'Swaps: ' + this.swaps) + .instruct(this.V.message, 0, 1, 'Comparisons: ' + this.comparisons); }; QuickSort.prototype = Object.create(Sorter.prototype); -QuickSort.prototype.instruct = function() { - this.instructions.push(arguments); - return this; -}; - // NOTE adds to an instruction set /** * */ QuickSort.prototype.sort = function(arr, start, end) { if (end - start <= 0) { - // this//.instruct('highlight', 500, end) - // .instruct('message', 0, 3, 'Start: ' + start) - // .instruct('message', 0, 4, 'End: ' + end) + // this//.instruct(this.V.highlight, 500, end) + // .instruct(this.V.message, 0, 3, 'Start: ' + start) + // .instruct(this.V.message, 0, 4, 'End: ' + end) return arr; } @@ -32,60 +31,82 @@ QuickSort.prototype.sort = function(arr, start, end) { var pivot = Math.floor((right + left) / 2); var pivotval = arr[pivot].value; var tmp; + var rval; + + this.V + .instruct(this.V.marker, 0, 1, left, 'L') + .instruct(this.V.marker, 0, 2, right, 'R') + + .instruct(this.V.showMarker, 0, 1) + .instruct(this.V.showMarker, 0, 2) - this//.instruct('initSection', left, right) - .instruct('showMarker', 0, 1) - .instruct('marker', 0, 1, left, 'L') - .instruct('message', 0, 3, 'Left: ' + left) - .instruct('showMarker', 0, 2) - .instruct('marker', 0, 2, right, 'R') - .instruct('message', 0, 4, 'Right: ' + right) - .instruct('unhighlight') - .instruct('highlight', 100, pivot) - .instruct('message', 0, 5, 'Pivot value: ' + pivotval + ', pivot index: ' + pivot); + .instruct(this.V.unhighlight, 0) + .instruct(this.V.highlight, 0, pivot) + + .instruct(this.V.message, 0, 3, 'Sorting from [' + start + '] to [' + end + ']') + .instruct(this.V.message, 0, 4, '') + .instruct(this.V.message, 100, 5, ''); while (left <= right) { - while (arr[left].value < pivotval) { - left++; + this.V.instruct(this.V.message, 0, 4, ''); + this.V.instruct(this.V.message, 0, 5, ''); + + while (arr[left].value < pivotval) { this.comparisons++; - this.instruct('marker', 100, 1, left) - .instruct('message', 0, 3, 'Left: ' + left) - .instruct('message', 0, 1, 'Comparisons: ' + this.comparisons) + this.V + .instruct(this.V.message, 0, 4, `${arr[left].value} < ${pivotval}, increment left` ) + .instruct(this.V.message, 0, 1, `Comparisons: ${this.comparisons}`) + .instruct(this.V.marker, 500, 1, left); + + left++; } - while (arr[right].value > pivotval) { - right--; + this.V + .instruct(this.V.message, 0, 4, `Left stop: ${arr[left].value} >= ${pivotval}`) + .instruct(this.V.marker, 500, 1, left); + while (arr[right].value > pivotval) { this.comparisons++; - this.instruct('marker', 100, 2, right) - .instruct('message', 0, 4, 'Right: ' + right) - .instruct('message', 0, 1, 'Comparisons: ' + this.comparisons) + this.V + .instruct(this.V.message, 0, 5, `${arr[right].value} < ${pivotval}, decrement right`) + .instruct(this.V.message, 0, 1, 'Comparisons: ' + this.comparisons) + + right--; + this.V.instruct(this.V.marker, 500, 2, right) } + this.V.instruct(this.V.message, 100, 5, `Right stop: ${arr[right].value} <= ${pivotval} `) + if (left <= right) { tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; - this.instruct('swap', 500, left, right); + this.V.instruct(this.V.swap, 500, left, right); left++; right--; this.swaps++; - this.instruct('message', 0, 2, 'Swaps: ' + this.swaps) - .instruct('marker', 100, 1, left) - .instruct('message', 0, 3, 'Left: ' + left) - .instruct('marker', 100, 2, right) - .instruct('message', 0, 4, 'Right: ' + right); + this.V + .instruct(this.V.message, 0, 2, 'Swaps: ' + this.swaps) + .instruct(this.V.message, 0, 4, 'Incremement left...') + .instruct(this.V.message, 0, 5, 'Decrement right...') + .instruct(this.V.marker, 100, 1, left) + .instruct(this.V.marker, 100, 2, right) + } } - this.instruct('unhighlight', 0) - .instruct('hideMarker', 0, 1) - .instruct('hideMarker', 0, 2); + this.V + .instruct(this.V.unhighlight, 0) + .instruct(this.V.hideMarker, 0, 1) + .instruct(this.V.hideMarker, 0, 2) + .instruct(this.V.message, 0, 3, 'Sorting complete.') + .instruct(this.V.message, 0, 4, '') + .instruct(this.V.message, 0, 5, ''); this.sort(arr, start, right); this.sort(arr, left, end); diff --git a/js/visualizer-actions.js b/js/visualizer-actions.js index 6aa902d..cdbd484 100644 --- a/js/visualizer-actions.js +++ b/js/visualizer-actions.js @@ -1,15 +1,12 @@ /** * */ -Visualizer.prototype.swap = function(args) { - var indexA = args[2]; - var indexB = args[3]; - +Visualizer.prototype.swap = function(delay, indexA, indexB) { // NOTE Two way binding here between dataset and function parameter? // NOTE swapping will not reorder index and i parameter will be off // NOTE discuss chained transitions: http://bl.ocks.org/mbostock/1125997 this.groups - .transition().duration(400) + .transition().duration(delay) .attr('transform', function doTransform(d) { if (d.index === indexA) { d.index = indexB; @@ -19,15 +16,13 @@ Visualizer.prototype.swap = function(args) { } return `translate(${Visualizer.calculateX(d.index)}, ${Visualizer.itemY})`; - }) + }); }; /** * Highlights an index. */ -Visualizer.prototype.highlight = function(args) { - var index = args[2]; - +Visualizer.prototype.highlight = function(delay, index) { if (index < 0) { console.error('Trying to highlight index of ' + index); return; @@ -47,12 +42,6 @@ Visualizer.prototype.unhighlight = function() { this.svg.selectAll('.item').attr('fill', function(d) { return d.fill; }); }; -// TODO restart instructions on re-init -// TODO add tabs for best/worst cases -// TODO add links to stats -// TODO fade unfade -// TODO try to pass reference instead of string, use apply() or call()? - // /** // * Greys out an item. // */ @@ -83,28 +72,21 @@ Visualizer.prototype.unhighlight = function() { /** * */ -Visualizer.prototype.hideMarker = function(args) { - var which = args[2]; +Visualizer.prototype.hideMarker = function(delay, which) { this.svg.select(`#marker${which}`).attr('style', 'display:none'); }; /** * */ -Visualizer.prototype.showMarker = function(args) { - var which = args[2]; +Visualizer.prototype.showMarker = function(delay, which) { this.svg.select(`#marker${which}`).attr('style', 'display:block'); }; /** * Marker movement. */ -Visualizer.prototype.marker = function(args) { - var delay = args[1]; - var which = args[2]; - var index = args[3]; - var label = args[4]; - +Visualizer.prototype.marker = function(delay, which, index, label) { if (label !== undefined) { label = label[0].toString(); this.svg.select(`#marker${which} text`).text(label) @@ -118,8 +100,31 @@ Visualizer.prototype.marker = function(args) { /** * Message updates. */ -Visualizer.prototype.message = function(args) { - var which = args[2]; - var msg = args[3]; +Visualizer.prototype.message = function(delay, which, msg) { + var msg = msg || ' '; this.parent.querySelector(`.message:nth-child(${which})`).innerHTML = msg; }; + +/** + * + */ +Visualizer.prototype.reset = function() { + this.instructionIndex = 0; + + this.message([null, 0, 1, '']); + this.message([null, 0, 2, '']); + this.message([null, 0, 3, '']); + this.message([null, 0, 4, '']); + this.message([null, 0, 5, '']); + + this.marker([null, 0, 1, 0]); + this.marker([null, 0, 2, 0]); + this.unhighlight(); + + this.groups + .transition().duration(100) + .attr('transform', function doTransform(d, i) { + d.index = i; + return `translate(${Visualizer.calculateX(i)}, ${Visualizer.itemY})`; + }); +}; diff --git a/js/visualizer-inits.js b/js/visualizer-inits.js index 1dbd03a..e6e0f27 100644 --- a/js/visualizer-inits.js +++ b/js/visualizer-inits.js @@ -1,3 +1,5 @@ +'use strict'; + /** * */ @@ -95,17 +97,15 @@ Visualizer.prototype.initMessages = function() { var div4 = document.createElement('div'); div4.className = 'message'; - div4.innerHTML = 'Comparisons: 0'; var div5 = document.createElement('div'); div5.className = 'message'; - div5.innerHTML = 'Swaps: 0'; - div1.innerHTML = 'testing'; - div2.innerHTML = 'testing'; - div3.innerHTML = 'testing'; - div4.innerHTML = 'testing'; - div5.innerHTML = 'testing'; + div1.innerHTML = ' '; + div2.innerHTML = ' '; + div3.innerHTML = ' '; + div4.innerHTML = ' '; + div5.innerHTML = ' '; container.appendChild(div1); container.appendChild(div2); @@ -153,22 +153,33 @@ Visualizer.prototype.initRange = function() { * */ Visualizer.prototype.initControls = function() { + var _this = this; + var updatePlayPause = function(paused) { if (paused === true) { - play.className = 'fa fa-pause'; + play.className = 'fa fa-play'; } else { - play.className = 'fa fa-play'; + play.className = 'fa fa-pause'; } }; var playclick = function() { this.paused = !this.paused; updatePlayPause(this.paused); - this.followInstruction(); + + if (this.instructionIndex === this.instructions.length) { + this.reset(); + this.paused = false; + this.followInstruction(); + } + else { + this.followInstruction(); + } }; var backclick = function() { + // TODO followinstruction skips delay of 0 this.paused = true; this.instructionIndex--; this.followInstruction(); @@ -183,8 +194,7 @@ Visualizer.prototype.initControls = function() { var restartclick = function() { this.paused = false; updatePlayPause(this.paused); - this.instructionIndex = 0; - this.followInstruction(); + this.reset(); }; var play = document.createElement('button'); diff --git a/js/visualizer.js b/js/visualizer.js index f9a1908..921988c 100644 --- a/js/visualizer.js +++ b/js/visualizer.js @@ -2,6 +2,7 @@ * */ function Visualizer(parent) { + this.instructions = []; this.parent = parent; this.sorter = null; this.paused = true; @@ -24,31 +25,31 @@ function Visualizer(parent) { switch(parent.attributes['data-algorithm'].value) { case 'quick': - this.sorter = new QuickSort(); + this.sorter = new QuickSort(this); break; case 'merge': - this.sorter = new MergeSort(); + this.sorter = new MergeSort(this); break; case 'selection': - this.sorter = new SelectionSort(); + this.sorter = new SelectionSort(this); break; case 'bubble': - this.sorter = new BubbleSort(); + this.sorter = new BubbleSort(this); break; case 'insertion': - this.sorter = new InsertionSort(); + this.sorter = new InsertionSort(this); break; case 'shell': - this.sorter = new ShellSort(); + this.sorter = new ShellSort(this); break; case 'radix': - this.sorter = new RadixSort(); + this.sorter = new RadixSort(this); break; default: @@ -58,49 +59,72 @@ function Visualizer(parent) { this.initItems(10); }; -// Public static properties (mutable) +// Static properties (mutable) Visualizer.spacerW = 5; Visualizer.itemW = 14; Visualizer.itemH = 50; Visualizer.itemY = 20; /** - * + * Static. */ Visualizer.calculateX = function(index) { return Visualizer.spacerW + index * (Visualizer.itemW + Visualizer.spacerW) }; +/** + * + */ +Visualizer.prototype.instruct = function() { + this.instructions.push(arguments); + return this; +}; + /** * Instructions contain a string with the name of a function in this object which is called to perform an action. */ Visualizer.prototype.followInstruction = function() { - if (this.instructionIndex >= this.sorter.instructions.length) { + if (this.instructionIndex >= this.instructions.length) { return; } - var instruction = this.sorter.instructions[this.instructionIndex]; - var operation = this[instruction[0]]; + var obj = this.instructions[this.instructionIndex]; + var instruction = new Array(); + + for (var key in obj) { + if (obj[key].hasOwnProperty) { + instruction.push(obj[key]); + } + } + var delay = instruction[1]; + var args = instruction.slice(1); - console.log(instruction); + // TODO finalize play button behavior + // TODO add tabs for best/worst cases + // TODO add links to stats + // TODO fade unfade + // TODO heap sort + // TODO extra memory // NOTE interesting (anti?)pattern here. // NOTE use of call() vs apply() (apply only delivered first array item as string) - if (typeof operation === 'function') { - operation.call(this, instruction); + // if (typeof operation === 'function') { + // operation.call(this, instruction); - if (delay === 0) { - this.instructionIndex++; - this.followInstruction(); - } - else if (this.paused === false) { - this.instructionIndex++; - setTimeout(this.followInstruction.bind(this), delay); - } + instruction[0].apply(this, args); + + if (delay === 0) { + this.instructionIndex++; + this.followInstruction(); } - else { - console.error(i); - throw new Error('Unidentified instruction.'); + else if (this.paused === false) { + this.instructionIndex++; + setTimeout(this.followInstruction.bind(this), delay); } + // } + // else { + // console.error(i); + // throw new Error('Unidentified instruction.'); + // } };