/* 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 Z 2
#define T 3 /* for keyframing */
#define FIRST 0 /* control points per knot */
#define SECOND 1
/* Solves a tridiagonal system for one of coordinates of Bezier control points. */
static double *GetFirstControlPoints (double *rhs, int Length)
{
int i, n = Length;
double *x = malloc (sizeof (double) * n /* or Length? */); /* Solution vector. */
double *tmp = malloc (sizeof (double) * n /* or Length? */); /* Temp workspace. */
double b = 2.0;
x[0] = rhs[0] / b;
for (i = 1; i < n; i++) { /* Decomposition and forward substitution. */
tmp[i] = 1 / b;
b = (i < n - 1 ? 4.0 : 2.0) - tmp[i];
x[i] = (rhs[i] - x[i - 1]) / b;
}
for (i = 1; i < n; i++)
x[n - i - 1] -= tmp[n - i] * x[n - i]; /* Backsubstitution. */
free(tmp);
return x;
}
/* Get open-ended Bezier Spline Control Points. */
static void GetCurveControlPoints (double **knots,
double ****ControlPointsP,
int Length) {
#define ControlPoints (*ControlPointsP)
int i, n = Length - 1, dim, order;
double *rhs, *xy[DIMENSIONS];
if (n < 1) {
for (order = FIRST; order <= SECOND; order++) ControlPoints[order] = NULL;
return;
}
/* Calculate Bezier control points */
rhs = malloc (sizeof (double) * Length /* or n? */); /* Right hand side vector */
for (dim = X; dim < DIMENSIONS; dim++) {
/* Set right hand side X or Y values */
for (i = 1; i < n - 1; ++i)
rhs[i] = 4 * knots[i][dim] + 2 * knots[i + 1][dim];
rhs[0] = knots[0][dim] + 2 * knots[1][dim];
rhs[n - 1] = 3 * knots[n - 1][dim];
xy[dim] = GetFirstControlPoints (rhs, n /* or Length? */);
}
/* Fill output arrays. */
for (order = FIRST; order <= SECOND; order++) {
ControlPoints[order] = malloc (sizeof (double *) * Length);
for (i = 0; i < Length /* Must be 'Length'! */; ++i)
ControlPoints[order][i] = malloc (sizeof (double) * DIMENSIONS);
}
for (i = 0; i < n; ++i)
for (dim = X; dim < DIMENSIONS; dim++) {
ControlPoints[FIRST][i][dim] = xy[dim][i];
ControlPoints[SECOND][i][dim] = (i < n - 1) ? 2 * knots[i + 1][dim] - xy[dim][i + 1] : (knots[n][dim] + xy[dim][n - 1]) / 2;
}
#undef ControlPoints
}
/* 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. */
double 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 */
double *delta) /* Parameter 0 <= t <= 1 */
{
double s = (max_slots-1) - t;
double AB = (A*s + B*t)/max_slots;
double BC = (B*s + C*t)/max_slots;
double CD = (C*s + D*t)/max_slots;
// double ABC = (AB*s + CD*t)/max_slots; - this 'bug fix' was wrong...
double ABC = (AB*s + BC*t)/max_slots; // original code was correct...
double BCD = (BC*s + CD*t)/max_slots;
if (delta) *delta = ABC-BCD;
return (ABC*s + BCD*t)/max_slots;
}
double **letter;
double ***controlpt;
void curve(int p1, int p2) {
double xy[DIMENSIONS];
int step, dim;
for (step = 0; step < 512; step++) {
for (dim = X; dim < DIMENSIONS; dim++)
xy[dim] = bezier(letter[p1][dim], controlpt[FIRST][p1][dim], controlpt[SECOND][p1][dim], letter[p2][dim], step, 512, NULL);
glVertex2f(xy[X], xy[Y]); // fprintf(stderr, " (%f,%f)\n", xy[X], xy[Y]);
}
}
void
bitmap_output(int x, int y, char *string, void *font)
{
glRasterPos2f(x, y);
while (*string) glutBitmapCharacter(font, *string++);
}
/*
* GL01Hello.cpp: Test OpenGL/GLUT C/C++ Setup
* Tested under Eclipse CDT with MinGW/Cygwin and CodeBlocks with MinGW
* To compile with -lfreeglut -lglu32 -lopengl32
*/
/* Handler for window-repaint event. Call back when the window first appears and
whenever the window needs to be re-painted. */
void display() {
#define TEST_SIZE 5
int i;
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);
for (i = 0; i < TEST_SIZE-1; i++) {
//fprintf(stderr, "%d: (%f,%f) (%f,%f) (%f,%f)\n",
// i, letter[i][X], letter[i][Y],
// controlpt[FIRST][i][X], controlpt[FIRST][i][Y],
// controlpt[SECOND][i][X], controlpt[SECOND][i][Y]);
if (i < TEST_SIZE-2) curve(i, i+1);
}
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) {
int i;
controlpt = malloc(sizeof(double **) * 2 /* first and second control points */);
letter = malloc(sizeof(double *) * TEST_SIZE);
for (i = 0; i < TEST_SIZE; i++)
letter[i] = malloc(sizeof(double) * DIMENSIONS);
/* Trivial test */
letter[0][X] = 10*4; letter[0][Y] = 0*4+20;
letter[1][X] = 10*4; letter[1][Y] = 50*4+20;
letter[2][X] = 28*4; letter[2][Y] = 50*4+20;
letter[3][X] = 30*4; letter[3][Y] = 0*4+20;
letter[4][X] = 10*4; letter[4][Y] = 0*4+20;
GetCurveControlPoints (letter, &controlpt, TEST_SIZE-1);
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);
}