/* 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>
#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 version 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 < 256; 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, 256, NULL);
fprintf(stderr, " (%f,%f)\n", xy[X], xy[Y]);
}
}
int main(int argc, char **argv) {
#define TEST_SIZE 5
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; letter[0][Y] = 0;
letter[1][X] = 10; letter[1][Y] = 50;
letter[2][X] = 28; letter[2][Y] = 50;
letter[3][X] = 30; letter[3][Y] = 0;
letter[4][X] = 10; letter[4][Y] = 0;
GetCurveControlPoints (letter, &controlpt, TEST_SIZE-1);
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);
}
exit(0);
return(0);
}