Skip to content

Instantly share code, notes, and snippets.

@memononen
Created November 23, 2014 19:05
Show Gist options
  • Save memononen/cef31ababbfa6cde5eea to your computer and use it in GitHub Desktop.
Save memononen/cef31ababbfa6cde5eea to your computer and use it in GitHub Desktop.
Drawing clipped anti-aliased graph using triangle strip
// Draws a graph using a triangle strip and simple gradient texture.
// Anti-aliasing may be off by a half a pixel.
// Works reasonable well with smooth as well as noisy data.
// The basic idea is that we draw the graph using a full height quad (or two triangles)
// per segment between two samples, and then we calculate texture coordinates
// so that 1px anti-aliasing gradient passes through the data points. The texture coordinate
// is calculated using the segment normal and one segment end point.
struct Point {
float x, y;
};
typedef struct Point Point;
#define MAX_POINTS 1000
static Point points[MAX_POINTS];
void drawGraph(float x, float y, float w, float h,
float tmin, float tmax, float vmin, float vmax,
float* values, int nvalues)
{
// Transform points
float trange = maxf(0.00001f, tmax - tmin);
float vrange = maxf(0.00001f, vmax - vmin);
int npoints = mini(nvalues, MAX_POINTS);
for (int i = 0; i < nvalues; i++) {
points[i].x = x + ((values[i*2+0] - tmin) / trange) * w;
points[i].y = (y + h-1) - ((values[i*2+1] - vmin) / vrange) * h; // top-left
}
// Draw background
glColor4ub(255,255,255,32);
glBegin(GL_QUADS);
glVertex2f(x,y+h);
glVertex2f(x+w,y+h);
glVertex2f(x+w,y);
glVertex2f(x,y);
glEnd();
// Draw graph
glEnable(GL_TEXTURE_2D);
glColor4ub(255,255,255,255);
glBegin(GL_TRIANGLE_STRIP);
for (int i = 0; i < npoints-1; i++) {
float x0 = points[i].x;
float x1 = points[i+1].x;
float y0 = points[i].y;
float y1 = points[i+1].y;
// Skip invisible segments
if (x1 < x) continue;
if (x0 > x+w) continue;
// Clipped Quad for this segment
Point pts[4] = { {maxf(x, x0),y}, {maxf(x, x0),y+h}, {minf(x+w, x1),y}, {minf(x+w, x1),y+h}, };
// Calculate texture coordinates
float dx = x1 - x0;
float dy = y1 - y0;
float len = sqrtf(dx*dx + dy*dy);
if (len > 0) {
dx /= len;
dy /= len;
}
for (int j = 0; j < 4; j++) {
float v = -dy*(pts[j].x - x0) + dx*(pts[j].y - y0);
glTexCoord2f(0, v);
glVertex2f(pts[j].x, pts[j].y);
}
}
glEnd();
glDisable(GL_TEXTURE_2D);
}
// Texture
unsigned char data[4*4] = {
0,0,0,0,
85,85,85,85,
171,171,171,171,
255,255,255,255,
};
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 4, 4, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Test
const int nsine = 32;
float sine[32*2];
for (int i = 0; i < nsine; i++) {
float u = i/(float)(nsine-1);
sine[i*2] = u*100;
sine[i*2+1] = sinf(u*M_PI*2);
}
drawGraph(100,300, 400,80,
0,100, -1,1,
sine, nsine);
// Works ok with noisy data too
const int nnoise = 100;
float noise[100*2];
for (int i = 0; i < nnoise; i++) {
float u = i/(float)(nnoise-1);
noise[i*2] = u*100;
noise[i*2+1] = (float)rand()/(float)RAND_MAX;
}
drawGraph(100,200, 400,80,
0,400, 0,1,
noise, nnoise);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment