#define SP_PRECISION 0.5
int spStarted = FALSE;
double spStartX = 0, spStartY = 0;
double spPenX = 0, spPenY = 0;
void spSkipSpace(const char **pos)
{ while(**pos && isspace(**pos)) ++*pos; }
double spReadNum(const char **pos) {
spSkipSpace(pos);
char *end = "";
double x = strtod(*pos, &end);
*pos = end;
return x;
}
double spMax(double a, double b)
{ return a > b ? a : b; }
void spMoveTo(double x, double y) {
if (spStarted) strokePath();
spStarted = TRUE;
moveTo(x, y);
spStartX = spPenX = x;
spStartY = spPenY = y;
}
void spLineTo(double x, double y) {
if (!spStarted) spMoveTo(spPenX, spPenY);
lineTo(x, y);
spPenX = x;
spPenY = y;
}
void spClose() {
if (spStarted) { lineTo(spStartX, spStartY); closePath(); }
spMoveTo(spStartX, spStartY);
}
void spQuadraticTo(double x1, double y1, double x2, double y2) {
double dx = spMax(x1 - spPenX, x2 - spPenX);
double dy = spMax(y1 - spPenY, y2 - spPenY);
if (dx*dx + dy*dy <= SP_PRECISION*SP_PRECISION) {
spLineTo(x2, y2);
return;
}
double xx0 = 0.5*(spPenX + x1);
double yy0 = 0.5*(spPenY + y1);
double xx1 = 0.5*(x1 + x2);
double yy1 = 0.5*(y1 + y2);
double xxx0 = 0.5*(xx0 + xx1);
double yyy0 = 0.5*(yy0 + yy1);
spQuadraticTo(xx0, yy0, xxx0, yyy0);
spQuadraticTo(xx1, yy1, x2, y2);
}
void spCurveTo(double x1, double y1, double x2, double y2, double x3, double y3) {
double dx = spMax(spMax(x1 - spPenX, x2 - spPenX), x3 - spPenX);
double dy = spMax(spMax(y1 - spPenY, y2 - spPenY), y3 - spPenY);
if (dx*dx + dy*dy <= SP_PRECISION*SP_PRECISION) {
spLineTo(x2, y2);
return;
}
double xx0 = 0.5*(spPenX + x1);
double yy0 = 0.5*(spPenY + y1);
double xx1 = 0.5*(x1 + x2);
double yy1 = 0.5*(y1 + y2);
double xx2 = 0.5*(x2 + x3);
double yy2 = 0.5*(y2 + y3);
double xxx0 = 0.5*(xx0 + xx1);
double yyy0 = 0.5*(yy0 + yy1);
double xxx1 = 0.5*(xx1 + xx2);
double yyy1 = 0.5*(yy1 + yy2);
double xxxx0 = 0.5*(xxx0 + xxx1);
double yyyy0 = 0.5*(yyy0 + yyy1);
spCurveTo(xx0, yy0, xxx0, yyy0, xxxx0, yyyy0);
spCurveTo(xxx1, yyy1, xx2, yy2, x3, y3);
}
void spParse(const char *path) {
double x1, x2, x3;
double y1, y2, y3;
const char *c = path;
spSkipSpace(&c);
while(*c) {
switch(*c++) {
case 'M':
x1 = spReadNum(&c);
y1 = spReadNum(&c);
spMoveTo(x1, y1);
break;
case 'm':
x1 = spPenX + spReadNum(&c);
y1 = spPenY + spReadNum(&c);
spMoveTo(x1, y1);
break;
case 'L':
x1 = spReadNum(&c);
y1 = spReadNum(&c);
spLineTo(x1, y1);
break;
case 'l':
x1 = spPenX + spReadNum(&c);
y1 = spPenY + spReadNum(&c);
spLineTo(x1, y1);
break;
case 'Q':
x1 = spReadNum(&c);
y1 = spReadNum(&c);
x2 = spReadNum(&c);
y2 = spReadNum(&c);
spQuadraticTo(x1, y1, x2, y2);
break;
case 'q':
x1 = spPenX + spReadNum(&c);
y1 = spPenY + spReadNum(&c);
x2 = spPenX + spReadNum(&c);
y2 = spPenY + spReadNum(&c);
spQuadraticTo(x1, y1, x2, y2);
break;
case 'C':
x1 = spReadNum(&c);
y1 = spReadNum(&c);
x2 = spReadNum(&c);
y2 = spReadNum(&c);
x3 = spReadNum(&c);
y3 = spReadNum(&c);
spCurveTo(x1, y1, x2, y2, x3, y3);
break;
case 'c':
x1 = spPenX + spReadNum(&c);
y1 = spPenY + spReadNum(&c);
x2 = spPenX + spReadNum(&c);
y2 = spPenY + spReadNum(&c);
x3 = spPenX + spReadNum(&c);
y3 = spPenY + spReadNum(&c);
spCurveTo(x1, y1, x2, y2, x3, y3);
break;
case 'Z':
case 'z':
spClose();
break;
default:
printf("unknown path command \'%c\'\n", *(c - 1));
}
spSkipSpace(&c);
}
}
void spTrack(const char *path, double *x, double *y) {
double sx = *x, sy = *y;
const char *c = path;
spSkipSpace(&c);
while(*c) {
switch(*c++) {
case 'M':
sx = (*x = spReadNum(&c));
sy = (*y = spReadNum(&c));
break;
case 'm':
sx = (*x += spReadNum(&c));
sy = (*y += spReadNum(&c));
break;
case 'C':
spReadNum(&c);
spReadNum(&c);
case 'Q':
spReadNum(&c);
spReadNum(&c);
case 'L':
*x = spReadNum(&c);
*y = spReadNum(&c);
break;
case 'c':
spReadNum(&c);
spReadNum(&c);
case 'q':
spReadNum(&c);
spReadNum(&c);
case 'l':
*x += spReadNum(&c);
*y += spReadNum(&c);
break;
case 'Z':
case 'z':
*x = sx;
*y = sy;
break;
default:
printf("unknown path command \'%c\'\n", *(c - 1));
}
spSkipSpace(&c);
}
}