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