Good work on instructions handling. Markers implemented.

master
ben-burlingham 10 years ago
parent 89a20ac073
commit 990c6f38ed
  1. 48
      index.html
  2. 39
      js/quicksort.js
  3. 5
      js/sorter.js
  4. 268
      js/visualizer-actions.js
  5. 78
      js/visualizer-inits.js
  6. 28
      js/visualizer.js
  7. BIN
      res/distproth-webfont.eot
  8. 2226
      res/distproth-webfont.svg
  9. BIN
      res/distproth-webfont.ttf
  10. BIN
      res/distproth-webfont.woff
  11. BIN
      res/distproth-webfont.woff2
  12. 16
      res/stylesheet.css

@ -7,6 +7,21 @@
<link rel="stylesheet" href="vendor/fontawesome/css/font-awesome.css">
<style type='text/css'>
/* https://color.adobe.com/create/color-wheel/?base=2&rule=Monochromatic&selected=4&name=My%20Color%20Theme&mode=rgb&rgbvalues=0.17972473935894973,0.30406031865629857,0.5,0.6594494787178995,0.7916562131964631,1,0.35944947871789945,0.6081206373125971,1,0.0014128559043085631,0.1949717379336841,0.5,0.28755958297431955,0.48649650985007775,0.8&swatchOrder=0,1,2,3,4 */
@font-face {
font-family: 'thin';
src: url('res/distproth-webfont.eot');
src: url('res/distproth-webfont.eot?#iefix') format('embedded-opentype'),
url('res/distproth-webfont.woff2') format('woff2'),
url('res/distproth-webfont.woff') format('woff'),
url('res/distproth-webfont.ttf') format('truetype'),
url('res/distproth-webfont.svg#district_prothin') format('svg');
font-weight: normal;
font-style: normal;
}
html, body {
font-family:sans-serif;
margin:0;
@ -36,7 +51,7 @@
width:80%;
}
.comment-container {
.message-container {
background:khaki;
display:inline-block;
height:100%;
@ -44,10 +59,8 @@
width:50%;
}
.msg {
.message-container .message {
font-size:13px;
font-weight:bold;
line-height:30px;
}
.top {
@ -58,6 +71,22 @@
height:100px;
}
.top .marker {
fill:#bbb;
}
.top .marker text {
fill:#2E4E7F;
font-size:10px;
}
#marker1 {
height:30px;
left:10px;
top:50px;
width:30px;
}
.controls-container {
display:inline-block;
height:100%;
@ -68,7 +97,7 @@
.controls-container button {
border:1px solid #fff;
background:#d4d4d4;
color:#aaa;
color:#2E4E7F;
cursor:pointer;
font-size:12px;
height:30px;
@ -81,8 +110,13 @@
.controls-container button:hover {
background:#bbb;
border:1px solid #aaa;
color:#888;
border:1px solid #2E4E7F;
}
.controls-container .stat {
font-size:13px;
font-weight:bold;
line-height:30px;
}
</style>
</head>

@ -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);
}
}

@ -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})`
});
};

@ -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;
};

@ -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;
};

@ -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);
};

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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;
}
Loading…
Cancel
Save