diff --git a/index.html b/index.html index 846886f..52788fc 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,21 @@ diff --git a/js/quicksort.js b/js/quicksort.js index 88d4c46..601edf8 100644 --- a/js/quicksort.js +++ b/js/quicksort.js @@ -20,9 +20,9 @@ QuickSort.prototype.instruct = function() { */ QuickSort.prototype.sort = function(arr, start, end) { if (end - start <= 0) { - this.instruct('highlight', end) - .instruct('message1', 'Start: ' + start) - .instruct('message2', 'End: ' + end) + // this.instruct('highlight', true, end) + // .instruct('message1', true, 'Start: ' + start) + // .instruct('message2', true, 'End: ' + end) return arr; } @@ -33,27 +33,30 @@ QuickSort.prototype.sort = function(arr, start, end) { var pivotval = arr[pivot].value; var tmp; - this.instruct('initSection', left, right) - .instruct('highlight', pivot) - .instruct('message3', 'Pivot value: ' + pivotval); + this//.instruct('initSection', left, right) + .instruct('marker1', true, left, 'L') + .instruct('marker2', true, right, 'R'); + // .instruct('unhighlight') + // .instruct('highlight', true, pivot) + // .instruct('message3', false, 'Pivot value: ' + pivotval); while (left <= right) { while (arr[left].value < pivotval) { left++; this.comparisons++; - this.instruct('marker1', left, 'Left') - .instruct('message1', 'Left: ' + left) - .instruct('stats1', 'Comparisons: ' + this.comparisons) + this.instruct('marker1', true, left) + .instruct('message1', false, 'Left: ' + left) + .instruct('stats1', false, 'Comparisons: ' + this.comparisons) } while (arr[right].value > pivotval) { right--; this.comparisons++; - this.instruct('marker2', right, 'Right') - .instruct('message2', 'Right: ' + right) - .instruct('stats1', 'Comparisons: ' + this.comparisons) + this.instruct('marker2', true, right) + .instruct('message2', false, 'Right: ' + right) + .instruct('stats1', false, 'Comparisons: ' + this.comparisons) } if (left <= right) { @@ -65,12 +68,12 @@ QuickSort.prototype.sort = function(arr, start, end) { right--; this.swaps++; - this.instruct('swap', left, right) - .instruct('stats2', 'Swaps: ' + this.swaps) - .instruct('message1', 'Left: ' + left) - .instruct('message2', 'Right: ' + right) - .instruct('marker1', left) - .instruct('marker2', right); + this//.instruct('swap', left, right) + .instruct('stats2', false, 'Swaps: ' + this.swaps) + .instruct('message1', false, 'Left: ' + left) + .instruct('message2', false, 'Right: ' + right) + .instruct('marker1', true, left) + .instruct('marker2', true, right); } } diff --git a/js/sorter.js b/js/sorter.js index d4df3df..03b3c9c 100644 --- a/js/sorter.js +++ b/js/sorter.js @@ -24,9 +24,12 @@ Sorter.prototype.shuffle = function(arr) { */ Sorter.prototype.generate = function(n) { var arr = []; + var v; for (var i = 0; i < n; i++) { + v = Math.floor(i * 255 / n); arr.push({ - value: Math.floor(i * 255 / n), + value: v, + fill: `rgb(0, 0, ${v})` }); }; diff --git a/js/visualizer-actions.js b/js/visualizer-actions.js index 9b7eb9a..7cbc372 100644 --- a/js/visualizer-actions.js +++ b/js/visualizer-actions.js @@ -1,125 +1,155 @@ /** * Instructions contain a string with the name of a function in this object which is called to perform an action. */ -Visualizer.prototype.actions = { - /** - * - */ - initSection: function() { - console.warn('INIT SECTION'); - }, - - /** - * - */ - swap: function(indexA, indexB) { - console.warn('SWAP'); - // Move up - // 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) - // .attr('transform', function doTransform(d) { - // if (d.index === indexA) { - // d.index = indexB; - // } - // else if (d.index === indexB) { - // d.index = indexA; - // } - - // return `translate(${d.index * (Visualizer.itemW + Visualizer.spacerW)}, ${Visualizer.itemY})`; - // }) - }, - - /** - * Highlights an index. - */ - highlight: function(index) { - console.warn('HIGHLIGHT'); - }, - - /** - * Un-highlights an index. - */ - unhighlight: function(index) { - console.warn('UNHIGHLIGHT'); - }, - - // /** - // * Greys out an item. - // */ - // fade: function(startIndex, endIndex) { - // console.log(`fading from ${startIndex} to ${endIndex}`) - - // this.svg.selectAll('rect') - // // NOTE this replaces the fill function reference for each rectangle - key point! - // .attr('fill', function fill(d) { - // if (d.index >= startIndex && d.index <= endIndex) { - // console.log(`${d.index} to ${d.fade}`) - // return d.fade; +Visualizer.prototype.initSection = function() { + // console.warn('INIT SECTION'); +}; + +/** + * + */ +Visualizer.prototype.swap = function(indexA, indexB) { + // console.warn('SWAP'); +// Move u; +// 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) + // .attr('transform', function doTransform(d) { + // if (d.index === indexA) { + // d.index = indexB; // } - // return d.fill; - // }); - // }; - - // /** - // * Restores all items to un-greyed state. - // */ - // unfade: function() { - // this.svg.selectAll('rect') - // .attr('fill', function fill(d) { - // console.log(`unfade ${d.index}`) - // return d.fill; + // else if (d.index === indexB) { + // d.index = indexA; + // } + + // return `translate(${d.index * (Visualizer.itemW + Visualizer.spacerW)}, ${Visualizer.itemY})`; // }) - // }; - - /** - * Moves marker 1 to an index and sets its text label. - */ - marker1: function(index, label) { - console.warn('MARKER1') - }, - - /** - * Moves marker 2 to an index and sets its text label. - */ - marker2: function(index, label) { - console.warn('MARKER2') - }, - - /** - * Updates message in stats 1 div. - */ - stats1: function(message) { - console.warn('STATS1') - }, - - /** - * Updates message in stats 2 div. - */ - stats2: function(message) { - console.warn('STATS2') - }, - - /** - * Updates message in message 1 div. - */ - message1: function(message) { - console.warn('MESSAGE1') - }, - - /** - * Updates message in message 2 div. - */ - message2: function(message) { - console.warn('MESSAGE2') - }, - - /** - * Updates message in message 3 div. - */ - message3: function(message) { - console.warn('MESSAGE3') - }, +}; + +/** + * Highlights an index. + */ +Visualizer.prototype.highlight = function(args) { + var index = args[2]; + + if (index < 0) { + console.error('Trying to highlight index of ' + index); + return; + } + + this.svg.select(`g:nth-child(${index})`).select('rect').attr('fill', 'red'); +}; + +/** + * Un-highlights an index. + */ +Visualizer.prototype.unhighlight = function() { + this.svg.selectAll('.item').attr('fill', function(d) { return d.fill; }); +}; + +// /** +// * Greys out an item. +// */ +// Visualizer.prototype.fade = function(startIndex, endIndex) { +// console.log(`fading from ${startIndex} to ${endIndex}`); +// this.svg.selectAll('rect') +// // NOTE this replaces the fill function reference for each rectangle - key point! +// .attr('fill', function fill(d) { +// if (d.index >= startIndex && d.index <= endIndex) { +// console.log(`${d.index} to ${d.fade}`) +// return d.fade; +// } +// return d.fill; +// }); +// }; + +// /** +// * Restores all items to un-greyed state. +// */ +// Visualizer.prototype.unfade = function() { +// this.svg.selectAll('rect') +// .attr('fill', functi;n fill(d) { +// console.log(`unfade ${d.index}`) +// return d.fill; +// }) +// }; + +/** + * Moves marker 1 to an index and sets its text label. + */ +Visualizer.prototype.marker1 = function(args) { + var index = args[2]; + var label = args[3]; + + if (label !== undefined) { + label = label[0].toString(); + this.svg.select('#marker1 text').text(label) + } + + var x = index * (Visualizer.itemW + Visualizer.spacerW); + + this.svg.select('#marker1') + .transition().duration(300) + .attr('transform', `translate(${x},0)`) +}; + +/** + * Moves marker 2 to an index and sets its text label. + */ +Visualizer.prototype.marker2 = function(args) { + var index = args[2]; + var label = args[3]; + + if (label !== undefined) { + label = label[0].toString(); + this.svg.select('#marker2 text').text(label) + } + + var x = index * (Visualizer.itemW + Visualizer.spacerW); + + this.svg.select('#marker2') + .transition().duration(300) + .attr('transform', `translate(${x},0)`) +}; + +/** + * Updates message in stats 1 div. + */ +Visualizer.prototype.stats1 = function(args) { + var msg = args[2]; + this.parent.querySelectorAll('.stat')[0].innerHTML = msg; +}; + +/** + * Updates message in stats 2 div. + */ +Visualizer.prototype.stats2 = function(args) { + var msg = args[2]; + this.parent.querySelectorAll('.stat')[1].innerHTML = msg; +}; + +/** + * Updates message in message 1 div. + */ +Visualizer.prototype.message1 = function(args) { + var msg = args[2]; + this.parent.querySelector('.message:nth-child(2)').innerHTML = msg; +}; + +/** + * Updates message in message 2 div. + */ +Visualizer.prototype.message2 = function(args) { + var msg = args[2]; + this.parent.querySelector('.message:nth-child(2)').innerHTML = msg; +}; + +/** + * Updates message in message 3 div. + */ +Visualizer.prototype.message3 = function(args) { + var msg = args[2]; + this.parent.querySelector('.message:nth-child(3)').innerHTML = msg; }; diff --git a/js/visualizer-inits.js b/js/visualizer-inits.js index 6a5abc5..9d13efb 100644 --- a/js/visualizer-inits.js +++ b/js/visualizer-inits.js @@ -9,23 +9,26 @@ Visualizer.prototype.initItems = function(n) { // A swap on the dataset will not take effect until after transition is complete, so custom index is required. var n = 0; - for (i in shuffled) { + for (var i in shuffled) { shuffled[i].index = n++; } + // Items this.groups = this.svg.selectAll('g').data(shuffled).enter().append('g') .attr('transform', `translate(0, ${Visualizer.itemY})`); this.groups.append('rect') + .attr('class', 'item') .attr('height', Visualizer.itemH) .attr('width', Visualizer.itemW) - .attr('fill', function doFill(d) { return `rgb(0, 0, ${d.value})`; }); + .attr('fill', function doFill(d) { return d.fill; }); this.groups.transition(500) .attr('transform', function doTransform(d, i) { return `translate(${i * (Visualizer.itemW + Visualizer.spacerW)}, ${Visualizer.itemY})`; }); + // Item labels this.groups.append('text') .text(function t(d) { return d.value; }) .attr('fill', '#aaa') @@ -35,24 +38,54 @@ Visualizer.prototype.initItems = function(n) { return `rotate(90 0,0), translate(5, -3)`; }); + // Markers + var m1 = this.svg.append('g') + .attr('class', 'marker') + .attr('id', 'marker1'); + + m1.append('rect') + .attr('height', Visualizer.itemW) + .attr('width', Visualizer.itemW); + + m1.append('text') + .attr('text-anchor', 'middle') + .attr('x', (Visualizer.itemW / 2)) + .attr('y', 10); + + var m2 = this.svg.append('g') + //temp + .attr('transform', 'translate(19,0)') + //temp + .attr('class', 'marker') + .attr('id', 'marker2'); + + m2.append('rect') + .attr('height', Visualizer.itemW) + .attr('width', Visualizer.itemW); + + m2.append('text') + .attr('text-anchor', 'middle') + .attr('x', (Visualizer.itemW / 2)) + .attr('y', 10); + setTimeout(this.followInstruction.bind(this, 0), 500); }; /** * */ -Visualizer.prototype.initComments = function() { +Visualizer.prototype.initMessages = function() { var container = document.createElement('div'); - container.className = 'comment-container'; + container.className = 'message-container'; var div1 = document.createElement('div'); - div1.className = 'comment'; + div1.className = 'message'; var div2 = document.createElement('div'); - div2.className = 'comment'; + div2.className = 'message'; var div3 = document.createElement('div'); - div3.className = 'comment'; + div3.className = 'message'; container.appendChild(div1); container.appendChild(div2); @@ -65,6 +98,15 @@ Visualizer.prototype.initComments = function() { * */ Visualizer.prototype.initRange = function() { + var rangeInput = function(label, event) { + label.innerHTML = 'Number of items (n) = ' + event.target.value; + }; + + var rangeChange = function(event) { + this.init.items(event.target.value); + }; + + var container = document.createElement('div'); container.className = 'range-container'; @@ -77,14 +119,14 @@ Visualizer.prototype.initRange = function() { range.setAttribute('value', 40); range.setAttribute('min', 5); range.setAttribute('max', 80); - range.addEventListener('change', this.rangeChange.bind(this)); - range.addEventListener('input', this.rangeInput.bind(null, msg)); + range.addEventListener('change', rangeChange.bind(this)); + range.addEventListener('input', rangeInput.bind(null, msg)); container.appendChild(range); container.appendChild(msg); return container; -}; +}, /** * @@ -110,13 +152,13 @@ Visualizer.prototype.initControls = function() { reset.title = 'Restart' reset.addEventListener('click', onclick); - var msg1 = document.createElement('div'); - msg1.className = 'msg'; - msg1.innerHTML = 'Swaps: 999'; + var stat1 = document.createElement('div'); + stat1.className = 'stat'; + stat1.innerHTML = 'Swaps: 999'; - var msg2 = document.createElement('div'); - msg2.className = 'msg'; - msg2.innerHTML = 'Comparisons: 999'; + var stat2 = document.createElement('div'); + stat2.className = 'stat'; + stat2.innerHTML = 'Comparisons: 999'; var container = document.createElement('div'); container.className = 'controls-container'; @@ -125,8 +167,8 @@ Visualizer.prototype.initControls = function() { container.appendChild(back); container.appendChild(play); container.appendChild(forward); - container.appendChild(msg1); - container.appendChild(msg2); + container.appendChild(stat1); + container.appendChild(stat2); return container; }; diff --git a/js/visualizer.js b/js/visualizer.js index e9c45fd..2df07f6 100644 --- a/js/visualizer.js +++ b/js/visualizer.js @@ -13,8 +13,8 @@ function Visualizer(parent) { var controls = this.initControls(); parent.querySelector('.bottom').appendChild(controls); - var comments = this.initComments(); - parent.querySelector('.bottom').appendChild(comments); + var messages = this.initMessages(); + parent.querySelector('.bottom').appendChild(messages); var range = this.initRange(); parent.querySelector('.bottom').appendChild(range); @@ -81,18 +81,18 @@ Visualizer.prototype.followInstruction = function(index) { console.log(i); // NOTE interesting pattern here - if (typeof this.actions[i[0]] === 'function') { - this.actions[i[0]](i); + if (typeof this[i[0]] === 'function') { + this[i[0]](i); - if (this.actions[i[1]] === true) { - setTimeout(this.followInstruction.bind(this, index + 1), 400); + if (i[1] === true) { + setTimeout(this.followInstruction.bind(this, index + 1), 400); } else { this.followInstruction(index + 1); } } // TODO document this - else if (this.actions[i[0]] !== undefined) { + else if (this[i[0]] !== undefined) { console.warn(this[i[0]]); } else { @@ -100,17 +100,3 @@ Visualizer.prototype.followInstruction = function(index) { throw new Error('Unidentified instruction.'); } }; - -/** - * - */ -Visualizer.prototype.rangeInput = function(label, event) { - label.innerHTML = 'Number of items (n) = ' + event.target.value; -}; - -/** - * - */ -Visualizer.prototype.rangeChange = function(event) { - this.init(event.target.value); -}; diff --git a/res/distproth-webfont.eot b/res/distproth-webfont.eot new file mode 100644 index 0000000..e2edfd0 Binary files /dev/null and b/res/distproth-webfont.eot differ diff --git a/res/distproth-webfont.svg b/res/distproth-webfont.svg new file mode 100644 index 0000000..3801804 --- /dev/null +++ b/res/distproth-webfont.svg @@ -0,0 +1,2226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/distproth-webfont.ttf b/res/distproth-webfont.ttf new file mode 100644 index 0000000..8d2467b Binary files /dev/null and b/res/distproth-webfont.ttf differ diff --git a/res/distproth-webfont.woff b/res/distproth-webfont.woff new file mode 100644 index 0000000..7f8586e Binary files /dev/null and b/res/distproth-webfont.woff differ diff --git a/res/distproth-webfont.woff2 b/res/distproth-webfont.woff2 new file mode 100644 index 0000000..f26af13 Binary files /dev/null and b/res/distproth-webfont.woff2 differ diff --git a/res/stylesheet.css b/res/stylesheet.css new file mode 100644 index 0000000..3631f04 --- /dev/null +++ b/res/stylesheet.css @@ -0,0 +1,16 @@ +/* Generated by Font Squirrel (http://www.fontsquirrel.com) on October 11, 2015 */ + + + +@font-face { + font-family: 'district_prothin'; + src: url('distproth-webfont.eot'); + src: url('distproth-webfont.eot?#iefix') format('embedded-opentype'), + url('distproth-webfont.woff2') format('woff2'), + url('distproth-webfont.woff') format('woff'), + url('distproth-webfont.ttf') format('truetype'), + url('distproth-webfont.svg#district_prothin') format('svg'); + font-weight: normal; + font-style: normal; + +} \ No newline at end of file