You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

270 lines
7.1 KiB

/**
*
*/
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.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 shuffled) {
shuffled[i].index = n++;
}
var svg = d3.select(this.parent.querySelector('.top')).append('svg');
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 `rgb(0, 0, ${d.value})`; });
this.groups.transition(500)
.attr('transform', function doTransform(d, i) {
return `translate(${i * (Visualizer.itemW + Visualizer.spacerW)}, ${Visualizer.itemY})`;
});
this.groups.append('text')
.text(function t(d) { return d.value; })
.attr('fill', '#aaa')
.attr('font-size', 10)
.attr('font-family', 'sans-serif')
.attr('transform', function doTransform(d) {
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);
};
/**
*
*/
Visualizer.prototype.swap = function(indexA, indexB) {
// 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})`;
})
};
/**
*
* /
Visualizer.prototype.moveMarker = function(id, toIndex) {
this.svg.select('#' + id).attr('x', toIndex * 22);
};
/**
*
* /
Visualizer.prototype.addMarker = function(id) {
// this.svg.append('text')
// .attr('id', id)
// .attr('x', 53)
// .attr('y', 50)
// .attr('width', 20)
// .attr('height', 20)
// .attr('fill', '#aaa')
// .attr('font-size', 20)
// .attr('alignment-baseline', 'middle')
// .attr('text-anchor', 'middle')
// .attr('transform', 'rotate(0 53,50)')
// .text('P');
};
/**
*
*/
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;
});
};
/**
*
*/
Visualizer.prototype.unfade = function() {
this.svg.selectAll('rect')
.attr('fill', function fill(d) {
console.log(`unfade ${d.index}`)
return d.fill;
})
};
/**
*
*/
Visualizer.prototype.followInstruction = function(instructions, index) {
if (index >= instructions.length) {
return;
}
var i = instructions[index];
console.log(i);
if (i.operation === 'swap' && i.left < i.right) {
this.swap(i.left, i.right);
setTimeout(this.followInstruction.bind(this, instructions, index + 1), 400);
}
else if (i.operation === 'init') {
// this.unfade();
// this.fade(0, i.left - 1);
// this.fade(i.right + 1, 1000)
this.followInstruction(instructions, index + 1);
}
else if (i.operation === 'swap') {
this.followInstruction(instructions, index + 1);
}
else {
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;
};