/* Bezier Spline methods from https://ovpwp.wordpress.com/2008/12/17/how-to-draw-a-smooth-curve-through-a-set-of-2d-points-with-bezier-methods/ */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <GL/glut.h> // GLUT, include glu.h and gl.h #define DIMENSIONS 2 /* add as many as you need ... */ #define X 0 #define Y 1 #define FIRST 0 /* control points per knot */ #define SECOND 1 #define MAX_POINTS 300 double letter[DIMENSIONS*MAX_POINTS]; // [x][y] [x][y] [x][y] [x][y] ... #define getletter(dim, index) letter[((index)*2)+(dim)] #define setletter(dim, index, val) letter[((index)*2)+(dim)] = (val) double ControlPoint[2*DIMENSIONS*MAX_POINTS]; // [first x][first y][second x][second y] [first x][first y][second x][second y] ... #define getpt(fs, dim, index) ControlPoint[((index)*4)+((fs)*2)+(dim)] #define setpt(fs, dim, index, val) ControlPoint[((index)*4)+((fs)*2)+(dim)] = (val) double xy[DIMENSIONS*MAX_POINTS]; #define getxy(dim, index) xy[(index)*2+(dim)] #define setxy(dim, index, val) xy[(index)*2+(dim)] = (val) double rhs[MAX_POINTS]; double tmp[MAX_POINTS]; double cpt[DIMENSIONS]; double b, t1,t2,t3,t4; int i, n, step, dim, order; // used in bezier procedure: double s; double AB; double BC; double CD; double ABC; double BCD; double bezier_delta; double bezier_result; /* The procedure below is based on */ /* http://stackoverflow.com/questions/5443653/opengl-coordinates-from-bezier-curves */ /* - it's a 1D Bezier than can be called twice to create a 2D Bezier spline and */ /* 3 times to create a 3D Bezier spline etc. Each axis is independent. */ void bezier (double A, /* Start value */ double B, /* First control value */ double C, /* Second control value */ double D, /* Ending value */ int t, int max_slots) /* eg 0..255/256 slots */ { /* Parameter 0 <= t <= 1 */ s = (max_slots - 1) - t; AB = (A * s + B * t) / max_slots; BC = (B * s + C * t) / max_slots; CD = (C * s + D * t) / max_slots; // ABC = (AB*s + CD*t)/max_slots; - this 'bug fix' was wrong... ABC = (AB * s + BC * t) / max_slots; // original code was correct... BCD = (BC * s + CD * t) / max_slots; bezier_delta = ABC - BCD; bezier_result = (ABC * s + BCD * t) / max_slots; } void curve (int p1, int p2) { for (step = 0; step < 512; step++) { for (dim = X; dim < DIMENSIONS; dim++) { t1 = getletter(dim, p1); t2 = getpt(FIRST, dim, p1); t3 = getpt(SECOND, dim, p1); t4 = getletter(dim, p2); bezier (t1, t2, t3, t4, step, 512); cpt[dim] = bezier_result; } glVertex2f (cpt[X], cpt[Y]); // fprintf(stderr, " (%f,%f)\n", xy[X], xy[Y]); } } /* Get open-ended Bezier Spline Control Points. */ static void DrawCurve (int Length) { n = Length - 1; if (n < 1) return; /* Calculate Bezier control points */ for (dim = X; dim < DIMENSIONS; dim++) { /* Set right hand side X or Y values */ for (i = 1; i < n - 1; ++i) { t1 = getletter(dim, i); t2 = getletter(dim, i + 1); rhs[i] = 4 * t1 + 2 * t2; } t1 = getletter(dim, 0); t2 = getletter(dim, 1); rhs[0] = t1 + 2 * t2; t1 = getletter(dim, n - 1); rhs[n - 1] = 3 * t1; /* Solves a tridiagonal system for one of coordinates of Bezier control points. */ b = 2.0; setxy(dim, 0, rhs[0] / b); for (i = 1; i < n; i++) { /* Decomposition and forward substitution. */ tmp[i] = 1 / b; if (i < n - 1) t1 = 4.0; else t1 = 2.0; b = t1 - tmp[i]; t1 = getxy(dim, i - 1); setxy(dim, i, (rhs[i] - t1) / b); } for (i = 1; i < n; i++) { t1 = getxy(dim, n - i - 1); t2 = getxy(dim, n - i); /* Backsubstitution. */ setxy(dim, n - i - 1, t1 - tmp[n - i] * t2); /* Backsubstitution. */ } } for (i = 0; i <= n; ++i) { for (dim = X; dim < DIMENSIONS; dim++) { t1 = getxy(dim, i); setpt(FIRST, dim, i, t1); if (i < n - 1) { t1 = getletter(dim, i + 1); t2 = getxy(dim, i + 1); t3 = 2 * t1 - t2; } else { t1 = getletter(dim, n); t2 = getxy(dim, n - 1); t3 = (t1 + t2) / 2; } setpt(SECOND, dim, i, t3); } if (i > 0) curve (i-1, i); } } void bitmap_output (int x, int y, char *string, void *font) { glRasterPos2f (x, y); while (*string) glutBitmapCharacter (font, *string++); } void display () { static int odd = 0; odd = (odd + 1) & 15; if (odd == 0) { glClearColor (0.0f, 0.0f, 0.0f, 1.0f); // Set background color to black and opaque glClear (GL_COLOR_BUFFER_BIT); // Clear the color buffer (background) glLineWidth (6.0); glColor3f (1.0, 1.0, 1.0); glBegin (GL_LINE_STRIP); /* Trivial test */ setletter(X, 0, 10 * 4); setletter(Y, 0, 0 * 4 + 20); setletter(X, 1, 10 * 4); setletter(Y, 1, 50 * 4 + 20); setletter(X, 2, 28 * 4); setletter(Y, 2, 50 * 4 + 20); setletter(X, 3, 30 * 4); setletter(Y, 3, 0 * 4 + 20); setletter(X, 4, 10 * 4); setletter(Y, 4, 0 * 4 + 20); setletter(X, 5, 10 * 4); setletter(Y, 5, 50 * 4 + 20); DrawCurve (4); glEnd (); bitmap_output (40, 275, "Letter 'n'", GLUT_BITMAP_9_BY_15); glFlush (); // Render now glutSwapBuffers (); } } void idle (void) { glutPostRedisplay (); } void reshape (int width, int height) { glViewport (0, 0, 480, 360); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho (0, 480, 0, 360, -1, 1); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); glEnable (GL_BLEND); glEnable (GL_LINE_SMOOTH); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } /* Main function: GLUT runs as a console application starting at main() */ int main (int argc, char **argv) { glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition (50, 50); glutInitWindowSize (480, 360); glutInit (&argc, argv); // Initialize GLUT glutCreateWindow ("Bezier path through several points"); // Create a window with the given title glutInitWindowSize (480, 360); // Set the window's initial width & height glViewport (0, 0, 480, 360); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glOrtho (0, 480, 0, 360, -1, 1); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); glEnable (GL_BLEND); glEnable (GL_LINE_SMOOTH); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glutInitWindowPosition (50, 50); // Position the window's initial top-left corner glutDisplayFunc (display); // Register display callback handler for window re-paint glutReshapeFunc (reshape); glutIdleFunc (idle); glutMainLoop (); // Enter the event-processing loop exit (0); return (0); }