Skip to content

Instantly share code, notes, and snippets.

@anselm
Created December 13, 2011 22:17
Show Gist options
  • Save anselm/1474156 to your computer and use it in GitHub Desktop.
Save anselm/1474156 to your computer and use it in GitHub Desktop.
A snippet of code to draw opengl ribbons given a series of connected line segments
// Note that I wrote this in java in Processing - paste this GIST into processing to see it in action.
// It is not the most elegant thing ever but it does deal with end caps and ribbon loops and the like.
// If you port this over to say objective-c / opengl / c etc - you may need to deal with polygon direction
// here is a snapshot of it running : http://www.flickr.com/photos/anselmhook/6507471719/in/photostream
int nvertices = 0;
float[] vertexpool = new float[3000];
float x1,x2,y1,y2;
void intersect(int kind, float x0, float y0, float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4) {
float det = ((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1));
if(det!=0) {
float ua = ((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/det;
// float ub = ((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1));
float x = x1 + ua*(x2-x1);
float y = y1 + ua*(y2-y1);
if(ua < 0 || ua > 1) {
if(kind == 3) {
// segments don't meet up soon enough; so pick a compromise between their ends
vertexpool[nvertices] = x2+(x3-x2)/2; nvertices++;
vertexpool[nvertices] = y2+(y3-y2)/2; nvertices++;
} else {
// an alternative is to put a non pointy cap onto the end rather than hacking it
vertexpool[nvertices] = x2; nvertices++;
vertexpool[nvertices] = y2; nvertices++;
vertexpool[nvertices] = x3; nvertices++;
vertexpool[nvertices] = y3; nvertices++;
}
} else {
vertexpool[nvertices] = x; nvertices++;
vertexpool[nvertices] = y; nvertices++;
}
} else {
// the lines are parallel... so just take the input end points
vertexpool[nvertices] = x0; nvertices++;
vertexpool[nvertices] = y0; nvertices++;
}
}
void ribbonAddVertex(float distance, float x3, float y3, int kind) {
if(kind==0) {
// begin saving up vertices of the line
x1 = x3;
y1 = y3;
return;
}
if(kind==1) {
// continue building up an understanding of the line
x2 = x3;
y2 = y3;
return;
}
if(kind>=2) {
// there are enough vertices at this time to add another ribbon segment
// for debugging - let's see the ribbon center
// stroke(random(155),random(200),random(255));
// line(x1,y1,x2,y2);
// line(x2,y2,x3,y3);
// get a perpendicular to this ribbon segment
float len1 = (float)Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
float nx1 = (x2-x1)/len1*distance;
float ny1 = (y2-y1)/len1*distance;
// get a perpendicular to the next ribbon segment
float len2 = (float)Math.sqrt((x3-x2)*(x3-x2)+(y3-y2)*(y3-y2));
float nx2 = (x3-x2)/len2*distance;
float ny2 = (y3-y2)/len2*distance;
// define 4 line segments; for the current segment and next segment ribbon edges
distance = distance / 6;
float xa1 = x1-ny1 - nx1*distance;
float ya1 = y1+nx1 - ny1*distance;
float xa2 = x1+ny1 - nx1*distance;
float ya2 = y1-nx1 - ny1*distance;
float xb1 = x2-ny1 + nx1*distance;
float yb1 = y2+nx1 + ny1*distance;
float xb2 = x2+ny1 + nx1*distance;
float yb2 = y2-nx1 + ny1*distance;
float xc1 = x2-ny2 - nx2*distance;
float yc1 = y2+nx2 - ny2*distance;
float xc2 = x2+ny2 - nx2*distance;
float yc2 = y2-nx2 - ny2*distance;
float xd1 = x3-ny2 + nx2*distance;
float yd1 = y3+nx2 + ny2*distance;
float xd2 = x3+ny2 + nx2*distance;
float yd2 = y3-nx2 + ny2*distance;
if(kind == 2) {
// the caller has asked for a cap on the start of the ribbon - give them one
vertexpool[nvertices] = xa1; nvertices++;
vertexpool[nvertices] = ya1; nvertices++;
vertexpool[nvertices] = xa2; nvertices++;
vertexpool[nvertices] = ya2; nvertices++;
}
// add segment intersection marking ribbon edge between the two skeleton lines
// an alternative way would be a function like atan2(slope1)+atan2(slope2) * distance
intersect(kind, x2-ny1,y2+nx1, xa1,ya1,xb1,yb1, xc1,yc1,xd1,yd1);
intersect(kind, x2+ny1,y2-nx1, xa2,ya2,xb2,yb2, xc2,yc2,xd2,yd2);
if(kind == 9) {
// the caller has asked for a cap on the end of the ribbon - give them one
vertexpool[nvertices] = xd1; nvertices++;
vertexpool[nvertices] = yd1; nvertices++;
vertexpool[nvertices] = xd2; nvertices++;
vertexpool[nvertices] = yd2; nvertices++;
}
// save previous ribbon skeleton line for next round
x1 = x2; y1 = y2;
x2 = x3; y2 = y3;
return;
}
}
void setup() {
size(400, 400);
background(0);
stroke(153);
nvertices = 0;
float d = 10;
// these two vertices begin the line but no triangles are added yet; marked as starts
ribbonAddVertex(d,50,100,0);
ribbonAddVertex(d,100,100,1);
// these vertices make up the body of the ribbon
ribbonAddVertex(d,100,50,3);
ribbonAddVertex(d,200,30,3);
ribbonAddVertex(d,220,60,4);
ribbonAddVertex(d,280,130,4);
ribbonAddVertex(d,300,200,4);
ribbonAddVertex(d,290,300,4);
ribbonAddVertex(d,280,200,4);
ribbonAddVertex(d,100,200,4);
ribbonAddVertex(d,50,200,3);
ribbonAddVertex(d,50,160,3);
ribbonAddVertex(d,50,120,3);
// these vertices repeat the beginning but with enough data to close the ribbon as a loop
ribbonAddVertex(d,50,100,3);
ribbonAddVertex(d,100,100,3);
ribbonAddVertex(d,100,50,3);
// let us see the ribbon
for(int i = 0; i <=nvertices-6; i+=2) {
float x1 = vertexpool[i+0];
float y1 = vertexpool[i+1];
float x2 = vertexpool[i+2];
float y2 = vertexpool[i+3];
float x3 = vertexpool[i+4];
float y3 = vertexpool[i+5];
stroke(0,200,200);
line(x1,y1,x2,y2);
line(x1,y1,x3,y3);
line(x3,y3,x2,y2);
}
}
void draw() {
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment