|
|
|
@ -1,37 +1,69 @@ |
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
var Visualizer = function() {}; |
|
|
|
|
function Visualizer(parent) { |
|
|
|
|
var top = document.createElement('div'); |
|
|
|
|
top.className = 'top'; |
|
|
|
|
parent.appendChild(top); |
|
|
|
|
|
|
|
|
|
var bottom = document.createElement('div'); |
|
|
|
|
bottom.className = 'bottom'; |
|
|
|
|
parent.appendChild(bottom); |
|
|
|
|
|
|
|
|
|
var range = this.initRange(); |
|
|
|
|
parent.querySelector('.bottom').appendChild(range); |
|
|
|
|
|
|
|
|
|
var controls = this.initControls(); |
|
|
|
|
parent.querySelector('.bottom').appendChild(controls); |
|
|
|
|
|
|
|
|
|
this.groups = null; |
|
|
|
|
this.parent = parent; |
|
|
|
|
this.init(10); |
|
|
|
|
|
|
|
|
|
// this.instructions = null;
|
|
|
|
|
// this.currentInstruction = 0;
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Public static properties (mutable)
|
|
|
|
|
Visualizer.spacerW = 5; |
|
|
|
|
Visualizer.itemW = 14; |
|
|
|
|
Visualizer.itemH = 50; |
|
|
|
|
Visualizer.itemY = 20; |
|
|
|
|
|
|
|
|
|
Visualizer.svg = null; |
|
|
|
|
Visualizer.groups = null; |
|
|
|
|
Visualizer.instructions = null; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
Visualizer.prototype.init = function(data) { |
|
|
|
|
Visualizer.prototype.init = function(n) { |
|
|
|
|
// Sorter setup.
|
|
|
|
|
var QS = new Quicksort(); |
|
|
|
|
var data = QS.generate(n); |
|
|
|
|
var shuffled = QS.shuffle(data); |
|
|
|
|
var ordered = Object.create(shuffled); |
|
|
|
|
QS.sort(ordered, 0, ordered.length - 1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var x = [] |
|
|
|
|
ordered.forEach(function(obj0) { |
|
|
|
|
x.push(obj0.value); |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
console.log(x.join(',')); |
|
|
|
|
|
|
|
|
|
// 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 data) { |
|
|
|
|
data[i].index = n++; |
|
|
|
|
for (i in shuffled) { |
|
|
|
|
shuffled[i].index = n++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
this.svg = d3.select('body').append('svg') |
|
|
|
|
.attr('class', 'quicksort-random'); |
|
|
|
|
var svg = d3.select(this.parent.querySelector('.top')).append('svg'); |
|
|
|
|
|
|
|
|
|
this.groups = this.svg.selectAll('g').data(data).enter().append('g') |
|
|
|
|
this.groups = svg.selectAll('g').data(shuffled).enter().append('g') |
|
|
|
|
.attr('transform', `translate(0, ${Visualizer.itemY})`); |
|
|
|
|
|
|
|
|
|
this.groups.append('rect') |
|
|
|
|
.attr('height', Visualizer.itemH) |
|
|
|
|
.attr('width', Visualizer.itemW) |
|
|
|
|
.attr('fill', function doFill(d) { return d.fill; }); |
|
|
|
|
.attr('fill', function doFill(d) { return `rgb(0, 0, ${d.value})`; }); |
|
|
|
|
|
|
|
|
|
this.groups.transition(500) |
|
|
|
|
.attr('transform', function doTransform(d, i) { |
|
|
|
@ -39,7 +71,7 @@ Visualizer.prototype.init = function(data) { |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
this.groups.append('text') |
|
|
|
|
.text(function t(d) { console.log(d.val); return d.val; }) |
|
|
|
|
.text(function t(d) { return d.value; }) |
|
|
|
|
.attr('fill', '#aaa') |
|
|
|
|
.attr('font-size', 10) |
|
|
|
|
.attr('font-family', 'sans-serif') |
|
|
|
@ -47,9 +79,16 @@ Visualizer.prototype.init = function(data) { |
|
|
|
|
return `rotate(90 0,0), translate(5, -3)`; |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// V.addMarker('quicksort-left-marker');
|
|
|
|
|
// V.moveMarker('quicksort-left-marker', 2);
|
|
|
|
|
|
|
|
|
|
// this.svg.append('sometext').id('left-marker'); L
|
|
|
|
|
// this.svg.append('sometext').id('right-marker'); R
|
|
|
|
|
// this.svg.append('sometext').id('pivot-marker'); triangle or arrow
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(this.followInstruction.bind(this, QS.instructions, 0), 500); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -60,7 +99,6 @@ Visualizer.prototype.swap = function(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
|
|
|
|
|
// TODO now that elements aren't cached, is there a need for .index?
|
|
|
|
|
this.groups |
|
|
|
|
.transition().duration(400) |
|
|
|
|
.attr('transform', function doTransform(d) { |
|
|
|
@ -156,3 +194,77 @@ Visualizer.prototype.followInstruction = function(instructions, index) { |
|
|
|
|
this.followInstruction(instructions, index + 1); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
Visualizer.prototype.initRange = function(Visualizer) { |
|
|
|
|
var container = document.createElement('div'); |
|
|
|
|
container.className = 'range-container'; |
|
|
|
|
|
|
|
|
|
var label = document.createElement('div'); |
|
|
|
|
label.innerHTML = "n: 40"; |
|
|
|
|
|
|
|
|
|
var range = document.createElement('input'); |
|
|
|
|
range.setAttribute('type', 'range'); |
|
|
|
|
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, label)); |
|
|
|
|
|
|
|
|
|
container.appendChild(range); |
|
|
|
|
container.appendChild(label); |
|
|
|
|
|
|
|
|
|
return container; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
Visualizer.prototype.rangeInput = function(label, event) { |
|
|
|
|
label.innerHTML = 'Items: ' + event.target.value; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
Visualizer.prototype.rangeChange = function(event) { |
|
|
|
|
this.init(event.target.value); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
*/ |
|
|
|
|
Visualizer.prototype.initControls = function() { |
|
|
|
|
var play = document.createElement('button'); |
|
|
|
|
play.className = 'fa fa-play'; |
|
|
|
|
play.title = 'Play' |
|
|
|
|
play.addEventListener('click', onclick); |
|
|
|
|
|
|
|
|
|
var back = document.createElement('button'); |
|
|
|
|
back.className = 'fa fa-step-backward'; |
|
|
|
|
back.title = 'Step Backward'; |
|
|
|
|
back.addEventListener('click', onclick); |
|
|
|
|
|
|
|
|
|
var forward = document.createElement('button'); |
|
|
|
|
forward.className = 'fa fa-step-forward' |
|
|
|
|
forward.title = 'Step Forward'; |
|
|
|
|
forward.addEventListener('click', onclick); |
|
|
|
|
|
|
|
|
|
var reset = document.createElement('button'); |
|
|
|
|
reset.className = 'fa fa-fast-backward'; |
|
|
|
|
reset.title = 'Restart' |
|
|
|
|
reset.addEventListener('click', onclick); |
|
|
|
|
|
|
|
|
|
var container = document.createElement('div'); |
|
|
|
|
container.className = 'controls'; |
|
|
|
|
|
|
|
|
|
container.appendChild(reset); |
|
|
|
|
container.appendChild(back); |
|
|
|
|
container.appendChild(play); |
|
|
|
|
container.appendChild(forward); |
|
|
|
|
|
|
|
|
|
return container; |
|
|
|
|
}; |
|
|
|
|