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.
178 lines
5.0 KiB
178 lines
5.0 KiB
/**
|
|
*
|
|
*/
|
|
var Visualizer = function() {
|
|
this.itemW = 20;
|
|
this.itemH = 50;
|
|
|
|
this.svg = null;
|
|
this.instructions = null;
|
|
};
|
|
Visualizer.prototype.svg = null;
|
|
Visualizer.prototype.instructions = null;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
Visualizer.prototype.init = function(data) {
|
|
// 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++;
|
|
}
|
|
|
|
this.svg = d3.select('body').append('svg')
|
|
.attr('class', 'quicksort-random');
|
|
|
|
this.svg.selectAll('rect').data(data).enter()
|
|
.append('rect')
|
|
.attr('height', function h(d) { return d.h; })
|
|
.attr('width', function w(d) { return d.w; })
|
|
.attr('fill', function fill(d) { return d.fill; })
|
|
.attr('y', function y(d) { return d.y; })
|
|
.attr('x', function x(d) { return d.x; })
|
|
.append('text')
|
|
.text(function t(d) { return d.val; })
|
|
.attr('fill', '#000')
|
|
.attr('x', function x(d) { return d.x; })
|
|
.transition(500)
|
|
.attr('x', function expand(d, i) {
|
|
data[i].x = i * (d.w + 2);
|
|
return data[i].x;
|
|
});
|
|
|
|
// 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
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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
|
|
this.svg.selectAll('rect')
|
|
// .transition()
|
|
// .duration(100)
|
|
// .attr('y', function ya(d) {
|
|
// if (d.index === indexA || d.index === indexB) {
|
|
// d.y = 5;
|
|
// }
|
|
|
|
// return d.y; // NOTE d[i].y has been modified too. But, the bound value in the dataset
|
|
// // will not be updated until after the chain is completed, so it has to be explicitly defined here.
|
|
// })
|
|
|
|
// Switch places
|
|
// NOTE discuss chained transitions: http://bl.ocks.org/mbostock/1125997
|
|
// TODO now that elements aren't cached, is there a need for .index?
|
|
.transition()
|
|
.duration(400)
|
|
.attr('x', function xa(d) {
|
|
if (d.index === indexA) {
|
|
d.index = indexB;
|
|
d.x = d.index * (d.w + 2);
|
|
}
|
|
else if (d.index === indexB) {
|
|
d.index = indexA;
|
|
d.x = d.index * (d.w + 2);
|
|
}
|
|
|
|
return d.x;
|
|
})
|
|
|
|
// Move down
|
|
// .transition()
|
|
// .duration(100)
|
|
// .attr('y', function ya(d) {
|
|
// if (d.index === indexA || d.index === indexB) {
|
|
// d.y = 20;
|
|
// }
|
|
|
|
// return d.y;
|
|
// });
|
|
};
|
|
|
|
/**
|
|
*
|
|
*/
|
|
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', 0)
|
|
.attr('y', 10)
|
|
.attr('width', 20)
|
|
.attr('height', 20)
|
|
.attr('fill', '#333')
|
|
.attr('font-size', 20)
|
|
.attr('alignment-baseline', 'middle')
|
|
.attr('text-anchor', 'middle')
|
|
.attr('transform', 'rotate(90 10,10)')
|
|
.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 || index > 25) {
|
|
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);
|
|
}
|
|
};
|
|
|