Blob Blame Raw

#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);
  }
}