fabmodules/src/core/svg_path.c

1256 wiersze
41 KiB
C

//
// svg_path.c
// convert SVG to path
//
// Neil Gershenfeld 9/21/13
// (c) Massachusetts Institute of Technology 2013
//
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose, but must
// acknowledge the fab modules project. Copyright is
// retained and must be preserved. The work is provided
// as is; no warranty is provided, and users accept all
// liability.
//
// todo
// gradient intensity
//
#include "fab.h"
#define SVG_MAX_FILE 1000000
#define SVG_MAX_TRANSFORM 100
double angle(double ux, double uy, double vx, double vy) {
double sign = (((ux*vy-uy*vx) > 0) ? 1 : -1);
return (sign*acos((ux*vx+uy*vy)/(sqrt(ux*ux+uy*uy)*sqrt(vx*vx+vy*vy))));
}
void parse_number_units(char *ptr, double *number, double *unit_scale, char *units) {
//
// parse quoted number and units
// return scale in mm/unit
//
char *start,*end;
start = strstr(ptr,"\"");
end = strstr(start+1,"\"");
char c1,c2;
c1 = *(end-1);
c2 = *(end-2);
if ((((c2 >= '0') && (c2 <= '9')) || (c2 == '.'))
&& (((c1 >= '0') && (c1 <= '9')) || (c1 == '.'))) {
*number = strtod(start+1,NULL);
strcpy(units,"px");
}
else {
units[0] = *(end-2);
units[1] = *(end-1);
units[2] = 0;
*(end-2) = ' ';
*(end-1) = ' ';
*number = strtod(start+1,NULL);
}
if (0 == strncmp(units,"px",2))
*unit_scale = 25.4*1.0/90.0; // Inkscape px default 90/inch
else if (0 == strncmp(units,"pt",2))
*unit_scale = 25.4*1.0/72.0;
else if (0 == strncmp(units,"in",2))
*unit_scale = 25.4;
else if (0 == strncmp(units,"mm",2))
*unit_scale = 1.0;
else if (0 == strncmp(units,"cm",2))
*unit_scale = 10.0;
else {
printf("svg_path: oops -- don't recognize unit %s\n",units);
exit(-1);
}
}
void next_number(char **ptr, double *number) {
//
// return next number after pointer
//
char haystack[] = "0123456789.-+";
char *end;
//
// find start
//
while (1) {
if (strchr(haystack,**ptr) != NULL)
break;
*ptr += 1;
}
//
// find end
//
end = *ptr;
while (1) {
if (strchr(haystack,*end) == NULL)
break;
end += 1;
}
//
// move pointer to end and return number
//
*number = strtod(*ptr,&end);
*ptr = end;
}
char next_element(char **ptr, char current_element) {
//
// return next path element after pointer
//
char number_haystack[] = "0123456789.-+";
char element_haystack[] = "mMlLhHvVcCsSaAzZ\"";
while (1) {
if (strchr(element_haystack,**ptr) != NULL)
return(**ptr);
else if (strchr(number_haystack,**ptr) != NULL)
return(current_element);
else
*ptr += 1;
}
}
void clear_transform(int *transform) {
//
// pop current transform from stack
//
*transform -= 1;
}
int hex_int(char chr) {
//
// return hex for char
//
if (chr == '0') return 0;
else if (chr == '1') return 1;
else if (chr == '2') return 2;
else if (chr == '3') return 3;
else if (chr == '4') return 4;
else if (chr == '5') return 5;
else if (chr == '6') return 6;
else if (chr == '7') return 7;
else if (chr == '8') return 8;
else if (chr == '9') return 9;
else if ((chr == 'a') || (chr == 'A')) return 10;
else if ((chr == 'b') || (chr == 'B')) return 11;
else if ((chr == 'c') || (chr == 'C')) return 12;
else if ((chr == 'd') || (chr == 'D')) return 13;
else if ((chr == 'e') || (chr == 'e')) return 14;
else if ((chr == 'f') || (chr == 'F')) return 15;
printf("svg_path: oops -- non-hex char\n");
exit(-1);
}
void set_intensity(char *start, int *zn, struct fab_vars *v, int zsign) {
char *end,*ptr;
int r,g,b;
float i;
//
// set zn from element intensity
//
end = strstr(start+1,">");
ptr = strstr(start,"stroke:");
if ((ptr != NULL) && (ptr < end)) {
//
// stroke found, check for hex encoding
//
ptr = strstr(ptr,"#");
if ((ptr != NULL) && (ptr < end)) {
//
// hex encoding found, set intensity
//
r = 16*hex_int(ptr[1]) + hex_int(ptr[2]);
g = 16*hex_int(ptr[3]) + hex_int(ptr[4]);
b = 16*hex_int(ptr[5]) + hex_int(ptr[6]);
if (zsign == 1)
i = (r+g+b)/(255.0+255.0+255.0);
else
i = 1.0 - (r+g+b)/(255.0+255.0+255.0);
*zn = i * (v->nz - 1);
}
else {
//
// hex encoding not found, set to bottom
//
*zn = 0;
}
}
else {
//
// stroke not found, set to bottom
//
*zn = 0;
}
}
void set_transform(char *start, int *transform, double transforms[SVG_MAX_TRANSFORM][6]) {
double a,b,c,d,e,f;
double pi = 4*atan(1.0);
char *end,*next,*ptr;
//
// push transform for current element onto stack
//
end = strstr(start+1,">");
next = strstr(start+1,"<");
if ((next != NULL) && (next < end))
end = next;
//
// check for transform
//
ptr = strstr(start,"transform");
if ((ptr != NULL) && (ptr < end)) {
//
// found, check for matrix
//
ptr = strstr(start,"matrix(");
if ((ptr != NULL) && (ptr < end)) {
//printf("trans: matrix\n");
next_number(&ptr,&a);
next_number(&ptr,&b);
next_number(&ptr,&c);
next_number(&ptr,&d);
next_number(&ptr,&e);
next_number(&ptr,&f);
transforms[*transform][0] = a;
transforms[*transform][1] = b;
transforms[*transform][2] = c;
transforms[*transform][3] = d;
transforms[*transform][4] = e;
transforms[*transform][5] = f;
*transform += 1;
return;
}
//
// check for translate
//
ptr = strstr(start,"translate(");
if ((ptr != NULL) && (ptr < end)) {
//printf("trans: translate\n");
next_number(&ptr,&e);
next_number(&ptr,&f);
transforms[*transform][0] = 1;
transforms[*transform][1] = 0;
transforms[*transform][2] = 0;
transforms[*transform][3] = 1;
transforms[*transform][4] = e;
transforms[*transform][5] = f;
*transform += 1;
return;
}
//
// check for scale
//
ptr = strstr(start,"scale(");
if ((ptr != NULL) && (ptr < end)) {
//printf("trans: scale\n");
next_number(&ptr,&a);
transforms[*transform][0] = a;
transforms[*transform][1] = 0;
transforms[*transform][2] = 0;
transforms[*transform][3] = a;
transforms[*transform][4] = 0;
transforms[*transform][5] = 0;
*transform += 1;
return;
}
//
// check for rotate
//
ptr = strstr(start,"rotate(");
if ((ptr != NULL) && (ptr < end)) {
//printf("trans: rotate\n");
next_number(&ptr,&a);
transforms[*transform][0] = cos(a*pi/180.0);
transforms[*transform][1] = sin(a*pi/180.0);
transforms[*transform][2] = -sin(a*pi/180.0);
transforms[*transform][3] = cos(a*pi/180.0);
transforms[*transform][4] = 0;
transforms[*transform][5] = 0;
*transform += 1;
return;
}
//
// check for skewX
//
ptr = strstr(start,"skewX(");
if ((ptr != NULL) && (ptr < end)) {
//printf("trans: skewX\n");
next_number(&ptr,&a);
transforms[*transform][0] = 1;
transforms[*transform][1] = 0;
transforms[*transform][2] = tan(a*pi/180.0);
transforms[*transform][3] = 1;
transforms[*transform][4] = 0;
transforms[*transform][5] = 0;
*transform += 1;
return;
}
//
// check for skewY
//
ptr = strstr(start,"skewY(");
if ((ptr != NULL) && (ptr < end)) {
//printf("trans: skewY\n");
next_number(&ptr,&a);
transforms[*transform][0] = 1;
transforms[*transform][1] = tan(a*pi/180.0);
transforms[*transform][2] = 0;
transforms[*transform][3] = 1;
transforms[*transform][4] = 0;
transforms[*transform][5] = 0;
*transform += 1;
return;
}
//
// didn't find transform
//
printf("svg_path: oops -- didn't find transform\n");
transforms[*transform][0] = 1;
transforms[*transform][1] = 0;
transforms[*transform][2] = 0;
transforms[*transform][3] = 1;
transforms[*transform][4] = 0;
transforms[*transform][5] = 0;
*transform += 1;
return;
}
else {
//
// transform not found, set unit transform
//
transforms[*transform][0] = 1;
transforms[*transform][1] = 0;
transforms[*transform][2] = 0;
transforms[*transform][3] = 1;
transforms[*transform][4] = 0;
transforms[*transform][5] = 0;
*transform += 1;
return;
}
}
void path_point(int *xn, int *yn, double x, double y, int transform, double transforms[SVG_MAX_TRANSFORM][6],
double xnscale, double ynscale, double xnmid, double vxmid, double ynmid, double vymid) {
double xt,yt,xtemp,ytemp;
int t;
//
// return path point
//
xt = x;
yt = y;
for (t = (transform-1); t >= 0; --t) {
xtemp = transforms[t][0]*xt + transforms[t][2]*yt + transforms[t][4];
ytemp = transforms[t][1]*xt + transforms[t][3]*yt + transforms[t][5];
xt = xtemp;
yt = ytemp;
}
*xn = xnmid + xnscale*(xt - vxmid);
*yn = ynmid + ynscale*(yt - vymid);
}
void fab_read_svg(struct fab_vars *v, char *input_file_name, float scale, int points, int resolution, float zmin, float zmax) {
//
// read SVG into fab_vars
//
FILE *input_file;
char buf[SVG_MAX_FILE];
char units[3];
char current_element,last_element;
int point,ret;
char *ptr,*start,*stop,*endptr;
int transform;
double transforms[SVG_MAX_TRANSFORM][6];
double unit_scale;
double xnscale,ynscale,xnmid,ynmid;
double vxmin,vymin,vxmid,vymid,vwidth,vheight,vaspect;
double width,height,aspect;
double x,y,z,x0,y0,x1,y1,x2,y2;
double rx,ry,theta,theta_0,theta_1,theta_diff,rotation,large_arc,sweep;
double phi,x1p,y1p,sign,cxp,cyp;
double ax,bx,cx,ay,by,cy,xt,yt,t,r;
double pi;
int xn,yn,zn;
int zsign;
int count;
pi = 4*atan(1.0);
//
// read SVG file
//
input_file = fopen(input_file_name, "rb");
if (input_file == 0) {
printf("svg_path.c: oops -- can't open %s\n",input_file_name);
exit(-1);
}
ret = fread(buf,1,SVG_MAX_FILE,input_file);
if (ret >= SVG_MAX_FILE) {
printf("svg_path: oops -- exceeded SVG buffer size\n");
exit(-1);
}
endptr = buf + ret;
fclose(input_file);
printf("read %d bytes from %s\n",ret,input_file_name);
//
// find SVG element
//
ptr = strstr(buf,"<svg");
if (ptr == NULL) {
printf("svg_path: oops -- no SVG element\n");
exit(-1);
}
stop = strstr(ptr,">");
//
// check for width and height
//
start = strstr(ptr,"width=");
if ((start == NULL) || (start > stop)) {
printf("svg_path: oops -- no width\n");
exit(-1);
}
parse_number_units(start,&width,&unit_scale,units);
printf(" width %f %s\n",width,units);
start = strstr(ptr,"height=");
if ((start == NULL) || (start > stop)) {
printf("svg_path: oops -- no height\n");
exit(-1);
}
parse_number_units(start,&height,&unit_scale,units);
printf(" height %f %s\n",height,units);
//
// check for viewBox
//
start = strstr(ptr,"viewBox=");
if ((start != NULL) && (start < stop)) {
next_number(&start,&vxmin);
next_number(&start,&vymin);
next_number(&start,&vwidth);
next_number(&start,&vheight);
printf(" view x min %f\n",vxmin);
printf(" view y min %f\n",vymin);
printf(" view width %f\n",vwidth);
printf(" view height %f\n",vheight);
aspect = height/width;
vaspect = vheight/vwidth;
vxmid = vxmin + vwidth/2.0;
vymid = vymin + vheight/2.0;
// assume xMidYMid meet scaling
if (vaspect > aspect) {
ynscale = resolution*aspect/vheight;
xnscale = ynscale;
}
else {
xnscale = resolution/vwidth;
ynscale = xnscale;
}
xnmid = resolution/2.0;
ynmid = aspect*xnmid;
}
else {
vxmid = width/2.0;
vymid = height/2.0;
xnscale = resolution/width;
ynscale = xnscale;
xnmid = xnscale*width/2.0;
ynmid = ynscale*height/2.0;
}
//
// start path
//
v->nx = 2*xnmid;
v->ny = 2*ynmid;
v->dx = scale*width*unit_scale;
v->dy = scale*height*unit_scale;
v->xmin = 0;
v->ymin = 0;
zsign = 1;
if (v->path->dof > 2) {
if (zmin > zmax) {
z = zmin;
zmin = zmax;
zmax = z;
zsign = -1;
}
v->nz = 1+xnscale*(zmax-zmin);
v->dz = zmax-zmin;
v->zmin = zmin;
}
//
// find graphic elements
//
ptr = buf;
transform = 0;
do {
ptr += 1;
if (strncmp(ptr,"<g",2) == 0) {
//
// opening g, check for transform and intensity
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
}
else if (strncmp(ptr,"/g>",2) == 0) {
//
// closing g, clear transform
//
clear_transform(&transform);
}
else if (strncmp(ptr,"<defs",5) == 0) {
//
// defs
//
while (1) {
//
// find closure
//
ptr +=1;
if (strncmp(ptr,"/>",2) == 0)
//
// self-closing
//
break;
else if (strncmp(ptr,">",1) == 0) {
//
// not self-closing
//
while (1) {
ptr += 1;
if (strncmp(ptr,"</defs>",7) == 0)
break;
}
break;
}
}
printf(" svg_path: defs not yet implemented\n");
}
else if (strncmp(ptr,"<image",6) == 0) {
//
// defs
//
while (1) {
//
// find closure
//
ptr +=1;
if (strncmp(ptr,"/>",2) == 0)
//
// self-closing
//
break;
else if (strncmp(ptr,">",1) == 0) {
//
// not self-closing
//
while (1) {
ptr += 1;
if (strncmp(ptr,"</image>",8) == 0)
break;
}
break;
}
}
printf(" svg_path: image not yet implemented\n");
}
else if (strncmp(ptr,"<path",5) == 0) {
//
// path
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
ptr = 4+strstr(ptr," d=");
current_element = 0;
last_element = 0;
x0 = 0;
y0 = 0;
do {
last_element = current_element;
current_element = next_element(&ptr, current_element);
if (current_element == 'm') {
//
// relative moveto
//
next_number(&ptr,&x);
next_number(&ptr,&y);
x0 = x0 + x;
y0 = y0 + y;
//printf(" path m: %f %f\n",x0,y0);
current_element = 'l';
fab_path_segment(v);
fab_path_point(v);
path_point(&xn,&yn,x0,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
else if (current_element == 'M') {
//
// absolute moveto
//
next_number(&ptr,&x0);
next_number(&ptr,&y0);
//printf(" path M: %f %f\n",x0,y0);
current_element = 'L';
fab_path_segment(v);
fab_path_point(v);
path_point(&xn,&yn,x0,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
else if (current_element == 'l') {
//
// relative lineto
//
next_number(&ptr,&x);
next_number(&ptr,&y);
//printf(" path l: %f %f\n",x,y);
current_element = 'l';
fab_path_point(v);
path_point(&xn,&yn,x0+x,y0+y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
x0 = x0+x;
y0 = y0+y;
}
else if (current_element == 'L') {
//
// absolute lineto
//
next_number(&ptr,&x);
next_number(&ptr,&y);
//printf(" path L: %f %f\n",x,y);
current_element = 'L';
fab_path_point(v);
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
x0 = x;
y0 = y;
}
else if (current_element == 'h') {
//
// relative horizontal
//
next_number(&ptr,&x);
//printf(" path h: %f\n",x);
current_element = 'j';
fab_path_point(v);
path_point(&xn,&yn,x0+x,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
x0 = x0+x;
}
else if (current_element == 'H') {
//
// absolute horizontal
//
next_number(&ptr,&x);
//printf(" path H: %f\n",x);
current_element = 'H';
fab_path_point(v);
path_point(&xn,&yn,x,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
x0 = x;
}
else if (current_element == 'v') {
//
// relative vertical
//
next_number(&ptr,&y);
//printf(" path v: %f\n",y);
current_element = 'v';
fab_path_point(v);
path_point(&xn,&yn,x0,y0+y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
y0 = y0+y;
}
else if (current_element == 'V') {
//
// absolute vertical
//
next_number(&ptr,&y);
//printf(" path V: %f\n",y);
current_element = 'V';
fab_path_point(v);
path_point(&xn,&yn,x0,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
y0 = y;
}
else if (current_element == 'c') {
//
// relative curveto
//
next_number(&ptr,&x1);
x1 += x0;
next_number(&ptr,&y1);
y1 += y0;
next_number(&ptr,&x2);
x2 += x0;
next_number(&ptr,&y2);
y2 += y0;
next_number(&ptr,&x);
x += x0;
next_number(&ptr,&y);
y += y0;
//printf(" path c: %f %f %f %f %f %f\n",x1,y1,x2,y2,x,y);
current_element = 'c';
cx = 3 * (x1 - x0);
bx = 3 * (x2 - x1) - cx;
ax = x - x0 - cx - bx;
cy = 3 * (y1 - y0);
by = 3 * (y2 - y1) - cy;
ay = y - y0 - cy - by;
for (point = 0; point < points; ++point) {
t = point / (points - 1.0);
xt = ax*t*t*t + bx*t*t + cx*t + x0;
yt = ay*t*t*t + by*t*t + cy*t + y0;
fab_path_point(v);
path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
x0 = x;
y0 = y;
}
else if (current_element == 'C') {
//
// absolute curveto
//
next_number(&ptr,&x1);
next_number(&ptr,&y1);
next_number(&ptr,&x2);
next_number(&ptr,&y2);
next_number(&ptr,&x);
next_number(&ptr,&y);
//printf(" path C: %f %f %f %f %f %f\n",x1,y1,x2,y2,x,y);
current_element = 'C';
cx = 3 * (x1 - x0);
bx = 3 * (x2 - x1) - cx;
ax = x - x0 - cx - bx;
cy = 3 * (y1 - y0);
by = 3 * (y2 - y1) - cy;
ay = y - y0 - cy - by;
for (point = 0; point < points; ++point) {
t = point / (points - 1.0);
xt = ax*t*t*t + bx*t*t + cx*t + x0;
yt = ay*t*t*t + by*t*t + cy*t + y0;
fab_path_point(v);
path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
x0 = x;
y0 = y;
}
else if (current_element == 's') {
//
// relative smooth curveto
//
if ((last_element == 'c') || (last_element == 'C') || (last_element == 's') || (last_element == 'S')) {
x1 = x0 + (x0-x2);
y1 = y0 + (y0-y2);
}
else {
x1 = x0;
y1 = y0;
}
next_number(&ptr,&x2);
x2 += x0;
next_number(&ptr,&y2);
y2 += y0;
next_number(&ptr,&x);
x += x0;
next_number(&ptr,&y);
y += y0;
//printf(" path s: %f %f %f %f\n",x2,y2,x,y);
current_element = 'c';
cx = 3 * (x1 - x0);
bx = 3 * (x2 - x1) - cx;
ax = x - x0 - cx - bx;
cy = 3 * (y1 - y0);
by = 3 * (y2 - y1) - cy;
ay = y - y0 - cy - by;
for (point = 0; point < points; ++point) {
t = point / (points - 1.0);
xt = ax*t*t*t + bx*t*t + cx*t + x0;
yt = ay*t*t*t + by*t*t + cy*t + y0;
fab_path_point(v);
path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
x0 = x;
y0 = y;
}
else if (current_element == 'S') {
//
// absolute smooth curveto
//
if ((last_element == 'c') || (last_element == 'C') || (last_element == 's') || (last_element == 'S')) {
x1 = x0 + (x0-x2);
y1 = y0 + (y0-y2);
}
else {
x1 = x0;
y1 = y0;
}
next_number(&ptr,&x2);
next_number(&ptr,&y2);
next_number(&ptr,&x);
next_number(&ptr,&y);
//printf(" path S: %f %f %f %f\n",x2,y2,x,y);
current_element = 'C';
cx = 3 * (x1 - x0);
bx = 3 * (x2 - x1) - cx;
ax = x - x0 - cx - bx;
cy = 3 * (y1 - y0);
by = 3 * (y2 - y1) - cy;
ay = y - y0 - cy - by;
for (point = 0; point < points; ++point) {
t = point / (points - 1.0);
xt = ax*t*t*t + bx*t*t + cx*t + x0;
yt = ay*t*t*t + by*t*t + cy*t + y0;
fab_path_point(v);
path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
x0 = x;
y0 = y;
}
else if (current_element == 'a') {
//
// relative arc
//
next_number(&ptr,&rx);
next_number(&ptr,&ry);
next_number(&ptr,&rotation);
next_number(&ptr,&large_arc);
next_number(&ptr,&sweep);
next_number(&ptr,&x);
x += x0;
next_number(&ptr,&y);
y += y0;
//printf(" path a: %f %f %f %f %f %f %f\n",rx,ry,rotation,large_arc,sweep,x,y);
current_element = 'a';
phi = rotation*pi/180.0;
x1 = x0;
x2 = x;
y1 = y0;
y2 = y;
x1p = cos(phi)*(x1-x2)/2.0 + sin(phi)*(y1-y2)/2.0;
y1p = -sin(phi)*(x1-x2)/2.0 + cos(phi)*(y1-y2)/2.0;
sign = ((large_arc == sweep) ? -1 : 1);
if ((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p) < 0) {
cxp = 0;
cyp = 0;
}
else {
cxp = sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * rx*y1p/ry;
cyp = -1 * sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * ry*x1p/rx;
}
cx = cos(phi)*cxp - sin(phi)*cyp + (x1+x2)/2.0;
cy = sin(phi)*cxp + cos(phi)*cyp + (y1+y2)/2.0;
theta_0 = angle(1,0,(x1p-cxp)/rx,(y1p-cyp)/ry);
theta_diff = angle ((x1p-cxp)/rx,(y1p-cyp)/ry,(-x1p-cxp)/rx,(-y1p-cyp)/ry);
theta_1 = theta_0 + theta_diff;
if (large_arc == 1) {
if (sweep == 0)
theta_1 -= 2*pi;
else
theta_1 += 2*pi;
}
for (point = 0; point < points; ++point) {
theta = theta_0 + (theta_1-theta_0) * point / (points - 1.0);
xt = cx + rx*cos(theta);
yt = cy + ry*sin(theta);
fab_path_point(v);
path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
x0 = x;
y0 = y;
}
else if (current_element == 'A') {
//
// absolute arc
//
next_number(&ptr,&rx);
next_number(&ptr,&ry);
next_number(&ptr,&rotation);
next_number(&ptr,&large_arc);
next_number(&ptr,&sweep);
next_number(&ptr,&x);
next_number(&ptr,&y);
//printf(" path A: %f %f %f %f %f %f %f\n",rx,ry,rotation,large_arc,sweep,x,y);
current_element = 'A';
phi = rotation*pi/180.0;
x1 = x0;
x2 = x;
y1 = y0;
y2 = y;
x1p = cos(phi)*(x1-x2)/2.0 + sin(phi)*(y1-y2)/2.0;
y1p = -sin(phi)*(x1-x2)/2.0 + cos(phi)*(y1-y2)/2.0;
sign = ((large_arc == sweep) ? -1 : 1);
if ((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p) < 0) {
cxp = 0;
cyp = 0;
}
else {
cxp = sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * rx*y1p/ry;
cyp = -1 * sign * sqrt((rx*rx*ry*ry - rx*rx*y1p*y1p - ry*ry*x1p*x1p)/(rx*rx*y1p*y1p + ry*ry*x1p*x1p)) * ry*x1p/rx;
}
cx = cos(phi)*cxp - sin(phi)*cyp + (x1+x2)/2.0;
cy = sin(phi)*cxp + cos(phi)*cyp + (y1+y2)/2.0;
theta_0 = angle(1,0,(x1p-cxp)/rx,(y1p-cyp)/ry);
theta_diff = angle ((x1p-cxp)/rx,(y1p-cyp)/ry,(-x1p-cxp)/rx,(-y1p-cyp)/ry);
theta_1 = theta_0 + theta_diff;
if (large_arc == 1) {
if (sweep == 0)
theta_1 -= 2*pi;
else
theta_1 += 2*pi;
}
for (point = 0; point < points; ++point) {
theta = theta_0 + (theta_1-theta_0) * point / (points - 1.0);
xt = cx + rx*cos(theta);
yt = cy + ry*sin(theta);
fab_path_point(v);
path_point(&xn,&yn,xt,yt,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
x0 = x;
y0 = y;
}
else if ((current_element == 'z') || (current_element == 'Z')) {
//
// closepath
//
//printf(" path zZ\n");
fab_path_point(v);
fab_path_axis(v,v->path->segment->first->first->value);
fab_path_axis(v,v->path->segment->first->first->next->value);
if (v->path->dof > 2)
fab_path_axis(v,zn);
current_element = 0;
ptr += 1;
}
} while (*ptr != '\"');
clear_transform(&transform);
}
else if (strncmp(ptr,"<rect",5) == 0) {
//
// rectangle
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
start = strstr(ptr,"width=");
next_number(&start,&width);
start = strstr(ptr,"height=");
next_number(&start,&height);
start = strstr(ptr,"x=");
next_number(&start,&x);
start = strstr(ptr,"y=");
next_number(&start,&y);
while (strncmp(ptr,"/>",2) != 0) ptr += 1; // read to end of element
//printf(" rect: %f %f %f %f\n",width,height,x,y);
fab_path_segment(v);
fab_path_point(v);
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
fab_path_point(v);
path_point(&xn,&yn,x,y+height,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
fab_path_point(v);
path_point(&xn,&yn,x+width,y+height,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
fab_path_point(v);
path_point(&xn,&yn,x+width,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
fab_path_point(v);
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
clear_transform(&transform);
}
else if (strncmp(ptr,"<circle",7) == 0) {
//
// circle
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
start = strstr(ptr,"cx=");
next_number(&start,&cx);
start = strstr(ptr,"cy=");
next_number(&start,&cy);
start = strstr(ptr,"r=");
next_number(&start,&r);
while (strncmp(ptr,"/>",2) != 0) ptr += 1; // read to end of element
//printf(" circle: %f %f %f\n",cx,cy,r);
fab_path_segment(v);
for (point = 0; point < points; ++point) {
fab_path_point(v);
x = cx + r*cos(point*2*pi/(points-1.0));
y = cy + r*sin(point*2*pi/(points-1.0));
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
}
clear_transform(&transform);
}
else if (strncmp(ptr,"<ellipse",8) == 0) {
//
// ellipse
//
while (strncmp(ptr,"/>",2) != 0) ptr += 1; // read to end of element
printf(" svg_path: ellipse not yet implemented\n");
}
else if (strncmp(ptr,"<line",5) == 0) {
//
// line
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
start = 3+strstr(ptr,"x1=");
next_number(&start,&x1);
start = 3+strstr(ptr,"y1=");
next_number(&start,&y1);
start = 3+strstr(ptr,"x2=");
next_number(&start,&x2);
start = 3+strstr(ptr,"y2=");
next_number(&start,&y2);
while (strncmp(ptr,"/>",2) != 0) ptr += 1; // read to end of element
//printf(" line: %f %f %f %f\n",x1,y1,x2,y2);
fab_path_segment(v);
fab_path_point(v);
path_point(&xn,&yn,x1,y1,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
fab_path_point(v);
path_point(&xn,&yn,x2,y2,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
clear_transform(&transform);
}
else if (strncmp(ptr,"<polyline",9) == 0) {
//
// polyline
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
ptr = 8+strstr(ptr,"points=");
//
// move to first point
//
next_number(&ptr,&x);
next_number(&ptr,&y);
fab_path_segment(v);
fab_path_point(v);
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
while (1) {
//
// loop over remaining points
//
next_number(&ptr,&x);
next_number(&ptr,&y);
fab_path_point(v);
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
while ((*ptr == ' ') || (*ptr == '"') || (*ptr == 13) || (*ptr == 10)) ptr += 1; // skip space
if (strncmp(ptr,"/>",2) == 0) break; // check for end
}
clear_transform(&transform);
}
else if (strncmp(ptr,"<polygon",8) == 0) {
//
// polygon
//
set_transform(ptr,&transform,transforms);
set_intensity(ptr,&zn,v,zsign);
ptr = 8+strstr(ptr,"points=");
//
// move to first point
//
next_number(&ptr,&x0);
next_number(&ptr,&y0);
fab_path_segment(v);
fab_path_point(v);
path_point(&xn,&yn,x0,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
while (1) {
//
// loop over remaining points
//
next_number(&ptr,&x);
next_number(&ptr,&y);
fab_path_point(v);
path_point(&xn,&yn,x,y,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
while ((*ptr == ' ') || (*ptr == '"') || (*ptr == 13) || (*ptr == 10)) ptr += 1; // skip space
if (strncmp(ptr,"/>",2) == 0) break; // check for end
}
fab_path_point(v);
path_point(&xn,&yn,x0,y0,transform,transforms,xnscale,ynscale,xnmid,vxmid,ynmid,vymid);
fab_path_axis(v,xn);
fab_path_axis(v,yn);
if (v->path->dof > 2)
fab_path_axis(v,zn);
clear_transform(&transform);
}
else if (strncmp(ptr,"<text",5) == 0) {
//
// text
//
while (strncmp(ptr,"/text>",6) != 0) ptr += 1; // read to end of element
printf(" svg_path: text not implemented\n");
}
} while ((endptr-ptr) > 1);
}
int main(int argc, char **argv) {
//
// local vars
//
struct fab_vars v;
init_vars(&v);
int points,resolution;
float scale,zmin,zmax;
//
// command line args
// Largo al Factotum Canadian Brass
//
if ((argc != 3) && (argc != 4) && (argc != 5) && (argc != 6) && (argc != 7) && (argc != 8)) {
printf("command line: svg_path in.svg out.path [scale [points [resolution [zmin [zmax]]]]]\n");
printf(" in.svg = input binary SVG file\n");
printf(" out.path = output path file\n");
printf(" scale = scale factor (optional, default 1.0)\n");
printf(" points = points per curve segment (optional, default 25)\n");
printf(" resolution = path x resolution (optional, default 10000)\n");
printf(" zmin = path min intensity z (optional, mm, default 0)\n");
printf(" zmax = path max intensity z (optional, mm, default zmin)\n");
exit(-1);
}
if (argc == 3) {
scale = 1.0;
points = 25;
resolution = 10000;
zmin = 0;
zmax = 0;
}
else if (argc == 4) {
sscanf(argv[3],"%f",&scale);
points = 25;
resolution = 10000;
zmin = 0;
zmax = 0;
}
else if (argc == 5) {
sscanf(argv[3],"%f",&scale);
sscanf(argv[4],"%d",&points);
resolution = 10000;
zmin = 0;
zmax = 0;
}
else if (argc == 6) {
sscanf(argv[3],"%f",&scale);
sscanf(argv[4],"%d",&points);
sscanf(argv[5],"%d",&resolution);
zmin = 0;
zmax = 0;
}
else if (argc == 7) {
sscanf(argv[3],"%f",&scale);
sscanf(argv[4],"%d",&points);
sscanf(argv[5],"%d",&resolution);
sscanf(argv[6],"%f",&zmin);
zmax = zmin;
}
else if (argc == 8) {
sscanf(argv[3],"%f",&scale);
sscanf(argv[4],"%d",&points);
sscanf(argv[5],"%d",&resolution);
sscanf(argv[6],"%f",&zmin);
sscanf(argv[7],"%f",&zmax);
}
//
// read SVG
//
if (argc <= 6)
fab_path_start(&v,2);
else
fab_path_start(&v,3);
fab_read_svg(&v,argv[1],scale,points,resolution,zmin,zmax);
//
// write path
//
fab_write_path(&v,argv[2]);
//
// return
//
return(0);
}