Bezier curves and mouse interaction in Processingjs


Demo here.

A lot has happened since last time, we now manage bezier curvature and mouse interaction. Since we now have interaction (out links will light up when we move the mouse over the site name in question) we can’t just quit in the middle of the draw function like we’ve done so far.

To achieve our goal we therefore have to split things up, we need to set everything up and store all the information we need in the setup function. This information will now be used in the draw function.

Let’s walk this stuff:

. . .
lmap.setup = function(){  
    p.size(800, 800);
    p.strokeWeight(2);
	lmap.arial = p.loadFont("arial.svg");
	lmap.font_size = 14;
	p.textFont(lmap.arial, lmap.font_size);
	
	var radius = 300;
	var pi_offset = p.TWO_PI / lmap.data.length;
	var angle = 0;
	var c_x = 400;
	var c_y = 400;
	
	$.each(lmap.data, function(i, obj){
		lmap.data[i].xpos = c_x + radius * p.cos(angle);
		lmap.data[i].ypos = c_y + radius * p.sin(angle);
		var txt_width = lmap.data[i].width = lmap.arial.width(obj.name) * lmap.font_size;
		lmap.data[i].height = lmap.font_size;
		lmap.data[i].angle = angle; 
		
		if(angle > p.PI/2 && angle < 3 * p.PI / 2){
			lmap.data[i].angle2 = angle + p.PI;
			lmap.data[i].xpos2 = -txt_width;
		}else{
			lmap.data[i].angle2 = angle;
			lmap.data[i].xpos2 = 0;
		}
		
		lmap.data[i].ypos2 = -lmap.font_size / 2;
		angle += pi_offset;
	});
	
	$.each(lmap.data, function(j, obj){
		lmap.data[j].object_links = [];
		$.each(obj.links, function(i, id){
			var to_obj = findPage(id);
			var obj_link = {};
			obj_link.to_obj = to_obj;
			
			var angle_diff = Math.abs(obj.angle - to_obj.angle) / 2;
			
			var short_radius = radius - 100;
			
			var rmin = -50 * angle_diff;
			var rmax = 50 * angle_diff;
			
			obj_link.xb1 = p.random(rmin, rmax) + c_x + short_radius * p.cos(obj.angle);
			obj_link.yb1 = p.random(rmin, rmax) + c_y + short_radius * p.sin(obj.angle);
			
			obj_link.xb2 = p.random(rmin, rmax) + c_x + short_radius * p.cos(to_obj.angle);
			obj_link.yb2 = p.random(rmin, rmax) + c_y + short_radius * p.sin(to_obj.angle);
			
			lmap.data[j].object_links.push(obj_link);
			
		});
	});
	
}
. . .

A lot here can be recognized from the old draw function, new stuff is happening in the second each loop. We need to store two extra points in order to be able to draw each curve properly, start and end point like before naturally, but also two new control points which will control the curvature.

To set the control points up we first use the new original angle value which we saved in the first loop, with that value we can define a disc drawn at the very same angle the text is drawn, but further inside the circle (100 pixels). The area of the disc is defined through the random max and min (rmin, rmax) values which currently stand at -50 to 50. Somewhere within this area we subsequently put the control point.

Finally we save all this information in a new object_links array, one object for each outgoing link.

. . .
function tFill(type, alpha){
	if(type == "external"){
		p.fill(150, 50, 50);
		p.stroke(150, 50, 50, alpha);
	}else{
		p.fill(50, 50, 150);
		p.stroke(50, 50, 150, alpha);
	}
}

function mouseOver(obj){
	if(p.dist(p.mouseX, p.mouseY, obj.xpos, obj.ypos) < obj.height)
		return true;
	return false;
}

lmap.draw = function(){
	p.background(255);
	$.each(lmap.data, function(i, obj){
		p.pushMatrix();
		p.translate(obj.xpos, obj.ypos);
		p.rotate(obj.angle2);
		p.translate(obj.xpos2, obj.ypos2);
		if(mouseOver(obj))
			tFill(obj.type, 100);
		else
			tFill(obj.type, 80);
		p.text(obj.name, 0, 0);
		p.popMatrix();
	});
	
	$.each(lmap.data, function(i, obj){
		$.each(obj.object_links, function(i, obj_link){
			var to_obj = obj_link.to_obj;
			if(mouseOver(obj))
				tFill(to_obj.type, 100);
			else
				tFill(to_obj.type, 50);
			p.bezier(obj.xpos, obj.ypos, obj_link.xb1, obj_link.yb1, obj_link.xb2, obj_link.yb2, to_obj.xpos, to_obj.ypos);
		});
	});
}
. . .

Not much new here, we need to check where the mouse is all the time, and we use bezier() to draw the curves with the help of the information we stored earlier.

If the mouseOver function returns true we will draw the lines using 100 for the alpha value, if not we use 50. Note that mouseOver is currently an imperfect cheat, we simply take the point where the curves terminate at the text and check if the mouse pointer is within a radius of the text height. Works for now but should really be fixed using some elementary linear algebra, like this example for instance.

And that was that, the next article will be the last one in this series, we will clean up and maybe add some niceties. We will definitely be adding links to the pages in question.


Related Posts

Tags: , , , , ,