Created
November 23, 2014 19:05
-
-
Save memononen/cef31ababbfa6cde5eea to your computer and use it in GitHub Desktop.
Drawing clipped anti-aliased graph using triangle strip
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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