I'm trying to make some kind of animation so that a user can understand or see the steps taken in finding the convex hull for a point set. For example, let's say I'm using this code below for Graham Scan, what are some ways to animate the line additions and removals? Even for a lot of points, it takes time to process and then plots them all almost immediately, and I'm unsure how to help the user experience what's going on...
function GrahamScan(points) {
points.sort(function(a, b){return a.x - b.x})
var stack1 = [];
var stack2 = [];
stack1.push(points[0])
stack1.push(points[1])
for (i=2; i < points.length; i++) {
len = stack1.length > 1;
turn = RTT(stack1[stack1.length-2], stack1[stack1.length-1], points[i]) === 1;
ctx.beginPath();
ctx.moveTo(stack1[stack1.length-2].x,stack1[stack1.length-2].y);
ctx.lineTo(stack1[stack1.length-1].x,stack1[stack1.length-1].y);
ctx.stroke();
while (len && !turn) {
stack1.pop();
reDraw(points, stack1, stack2);
len = stack1.length > 1;
if (!len) {
break
}
turn = RTT(stack1[stack1.length-2], stack1[stack1.length-1], points[i]) === 1;
}
stack1.push(points[i]);
}
ctx.beginPath();
ctx.moveTo(stack1[stack1.length-2].x,stack1[stack1.length-2].y);
ctx.lineTo(stack1[stack1.length-1].x,stack1[stack1.length-1].y);
ctx.stroke();
stack2 = [];
stack2.push(points[points.length-1])
stack2.push(points[points.length-2])
for (i=2; i < points.length; i++) {
len = stack2.length > 1;
turn = RTT(stack2[stack2.length-2], stack2[stack2.length-1], points[points.length-i-1]) === 1;
ctx.beginPath();
ctx.moveTo(stack2[stack2.length-2].x,stack2[stack2.length-2].y);
ctx.lineTo(stack2[stack2.length-1].x,stack2[stack2.length-1].y);
ctx.stroke();
while (len && !turn) {
stack2.pop();
reDraw(points, stack1, stack2);
len = stack2.length > 1;
if (!len) {
break
}
turn = RTT(stack2[stack2.length-2], stack2[stack2.length-1], points[points.length-i-1]) === 1;
}
stack2.push(points[points.length-i-1]);
}
ctx.beginPath();
ctx.moveTo(stack2[stack2.length-2].x,stack2[stack2.length-2].y);
ctx.lineTo(stack2[stack2.length-1].x,stack2[stack2.length-1].y);
ctx.stroke();
}
function reDraw(points,stack1,stack2) {
ctx.clearRect(0, 0, w, h);
document.getElementById("canvasimg").style.display = "none";
for (j = 0; j < points.length; j++) {
ctx.beginPath();
ctx.fillStyle = x;
ctx.fillRect(points[j].x-1, points[j].y-1, 3, 3);
ctx.closePath();
}
for (k = 1; k < stack1.length; k++) {
ctx.beginPath();
ctx.moveTo(stack1[k-1].x-1,stack1[k-1].y-1);
ctx.lineTo(stack1[k].x-1,stack1[k].y-1);
ctx.stroke();
}
for (l = 1; l < stack2.length; l++) {
ctx.beginPath();
ctx.moveTo(stack2[l-1].x-1,stack2[l-1].y-1);
ctx.lineTo(stack2[l].x-1,stack2[l].y-1);
ctx.stroke();
}
}
function RTT(a, b, c) {
return Math.sign((b.x - a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x));
}
Animating algorithms using generator functions.
The easiest way is to use a generator function to create place where you can stop execution of the algorithm and allow a animation loop to display and control the speed of execution. It does not interfere with the algorithm's function. See Generator function declaration MDN
Normal generator functions are used to generate data, but in this case we are not interested in the data but rather the visualization process built into the algorithm.
To animate just create a standard animation loop. Create a background canvas to hold any graphics that you don't want to draw each time you update / step the algorithm. Set a frame rate for the visualization, then every frame clear the canvas, draw the background, call for the next value from the generator function (which will render the next part of the algorithm) then wait for the next frame.
When the algorithm is complete the generator will return undefined as the value and you know it is complete.
A quick example
I have converted your
grahamScan
function into a generator function. Then create a generator withvis = grahamScan(points)
then I render the steps every 4 frames so that is ~15fps. I was not sure where you wanted the visual breaks and I also added some extra rendering as the found lines were flashing on and off ( inside the inner while loops the outer lines were not being drawn).I generate the points array randomly and restart the animation about 2 seconds after it is done.
The main animation loop is at the bottom, and I added some code to create random points and render them to a background canvas. The only limitation is the hull vert count if very high will slow it down. The points are pre rendered so will not affect the frame rate, you can have 100 of thousands to millions ( though pre render time will take a little time. I tested 500,000 points and it took about 4 seconds to render the background but the visualization ran at full frame rate.