#include "path2d.h"
BOOL RectContainsPoint(RECTDBL*rc,double x,double y){
return (x>=rc->left&&y>=rc->top&&x<rc->right&&y<rc->bottom);
}
BOOL RectIntersectsRect(RECTDBL*rc,double x,double y,double x1,double y1){
if(IsRectEmpty(rc)||x>=x1||y>=y1)
return FALSE;
return (x1>rc->left&&y1>rc->top&&x<rc->right&&y<rc->bottom);
}
BOOL RectContainsRect(RECTDBL*rc,double x,double y,double x1,double y1){
if(IsRectEmpty(rc)||x>=x1||y>=y1)
return FALSE;
return (x>=rc->left&&y>=rc->top&&x1<=rc->right&&y1<=rc->bottom);
}
BOOL ViewSpaceCalcTransformEx(
MATRIX*xfmDst,
RECTDBL*rcViewport,
RECTDBL*rcWindow,
int align,
int meetOrSlice
){
double xScale,yScale;
double result[6];
double vcx,vcy,wcx,wcy;
if(!xfmDst||!rcViewport||!rcWindow)
return FALSE;
vcx=RectWidth(rcViewport);
vcy=RectHeight(rcViewport);
wcx=RectWidth(rcWindow);
wcy=RectHeight(rcWindow);
if(vcx<0 || vcy<0 || wcx<0 || wcy<0){
MatrixSetIdentity(xfmDst);
return FALSE;
}
if(ISZERO(wcx)||ISZERO(wcy)){
xScale=0.0;
yScale=0.0;
} else {
xScale=(double)vcx/wcx;
yScale=(double)vcy/wcy;
}
result[MatrixM12]=result[MatrixM21]=0.0;
if(align==0){
result[MatrixM11]=xScale;
result[MatrixM22]=yScale;
result[MatrixDx]=(double)rcViewport->left-xScale*(double)rcWindow->left;
result[MatrixDy]=(double)rcViewport->top-yScale*(double)rcWindow->top;
}
else {
double xt,yt;
double xw,xh,x,y;
align-=1;
if(meetOrSlice){
result[MatrixM11]=max(xScale,yScale);
} else {
result[MatrixM11]=min(xScale,yScale);
}
result[MatrixM22]=result[MatrixM11];
xw=wcx*result[MatrixM11];
xh=wcy*result[MatrixM11];
switch (align % 3){
case 0:
x=0.0;
break;
case 1:
x=0.5*(vcx-xw);
break;
case 2:
x=(vcx-xw);
break;
}
switch (align/3){
case 0:
y=0.0;
break;
case 1:
y=0.5*(vcy-xh);
break;
case 2:
y=(vcy-xh);
break;
}
result[MatrixDx] =
(double)rcViewport->left-result[MatrixM11]*(double)rcWindow->left+x;
result[MatrixDy]=
(double)rcViewport->top-result[MatrixM22]*(double)rcWindow->top+y;
}
MatrixSetValueArray(xfmDst,result);
return TRUE;
}
inline double CubeRoot(double x){
if( x>=0.0 )return pow( x, 1.0/3.0 );
else return -pow( -x, 1.0/3.0 );
}
double PointDistance(double x1,double y1,double x2,double y2){
double dx=x1-x2;
double dy=y1-y2;
return sqrt(dx*dx+dy*dy);
}
double SegmentDistanceSq(
double x0, double y0, double x1, double y1,
double ptx, double pty
){
double a = x1-x0;
double b = y1-y0;
double px = ptx-x0;
double py = pty-y0;
double len = a*px+b*py;
double ret;
double c;
if(len<=0.0){
return px*px+py*py;
} else {
px=a-px;
py=b-py;
len=a*px+b*py;
ret=px*px+py*py;
if(len>0.0){
ret-=len*len/(a*a+b*b);
}
return ret;
}
}
double QuadFlatnessSq(double *c){
return SegmentDistanceSq(c[0],c[1],c[4],c[5],c[2],c[3]);
}
double CubicFlatnessSq(double *c){
double a=SegmentDistanceSq(c[0],c[1],c[6],c[7],c[2],c[3]);
double b=SegmentDistanceSq(c[0],c[1],c[6],c[7],c[4],c[5]);
return max(a,b);
}
void CubicCurveFromQuadratic(double *pSrc,double *pDst){
double a,b,c,d,e,f,g,h;
a=pSrc[0];
b=pSrc[1];
c=pSrc[0]+2*(pSrc[2]-pSrc[0])/3;
d=pSrc[1]+2*(pSrc[3]-pSrc[1])/3;
e=pSrc[2]+2*(pSrc[4]-pSrc[2])/3;
f=pSrc[3]+2*(pSrc[5]-pSrc[3])/3;
g=pSrc[4];
h=pSrc[5];
pDst[0]=a;
pDst[1]=b;
pDst[2]=c;
pDst[3]=d;
pDst[4]=e;
pDst[5]=f;
pDst[6]=g;
pDst[7]=h;
}
double PerpendicularDistance(
double x0, double y0, double x1, double y1,
double ptx, double pty
){
double a = -(y0 - y1);
double b = (x0 - x1);
double len= sqrt(a*a+b*b);
double c;
if(!ISZERO(len)){
a/=len;
b/=len;
}
c=-(a*x0+b*y0);
return a*ptx+b*pty+c;
}
void LineSubdivide(double *src,double *left,double *right,double t){
double x0=src[0];
double y0=src[1];
double x1=src[2];
double y1=src[3];
if(left){
left[0]=x0;
left[1]=y0;
}
if(right){
right[2]=x1;
right[3]=y1;
}
x1=x0+(x1-x0)*t;
y1=y0+(y1-y0)*t;
if(left){
left[2]=x1;
left[3]=y1;
}
if(right){
right[0]=x1;
right[1]=y1;
}
}
void QuadSubdivide(double *src,double *left,double *right,double t){
double x0=src[0];
double y0=src[1];
double cx=src[2];
double cy=src[3];
double x1=src[4];
double y1=src[5];
if(left){
left[0]=x0;
left[1]=y0;
}
if(right){
right[4]=x1;
right[5]=y1;
}
x1=cx+(x1-cx)*t;
y1=cy+(y1-cy)*t;
x0=x0+(cx-x0)*t;
y0=y0+(cy-y0)*t;
cx=x0+(x1-x0)*t;
cy=y0+(y1-y0)*t;
if(left){
left[2]=x0;
left[3]=y0;
left[4]=cx;
left[5]=cy;
}
if(right){
right[0]=cx;
right[1]=cy;
right[2]=x1;
right[3]=y1;
}
}
void CubicSubdivide(double *src,double *left,double *right,double t){
double x0=src[0];
double y0=src[1];
double cx0=src[2];
double cy0=src[3];
double cx1=src[4];
double cy1=src[5];
double x1=src[6];
double y1=src[7];
double cx,cy;
if(left){
left[0]=x0;
left[1]=y0;
}
if(right){
right[6]=x1;
right[7]=y1;
}
x1=cx1+(x1-cx1)*t;
y1=cy1+(y1-cy1)*t;
x0=x0+(cx0-x0)*t;
y0=y0+(cy0-y0)*t;
cx0=cx0+(cx1-cx0)*t;
cy0=cy0+(cy1-cy0)*t;
cx1=cx0+(x1-cx0)*t;
cy1=cy0+(y1-cy0)*t;
cx0=x0+(cx0-x0)*t;
cy0=y0+(cy0-y0)*t;
cx=cx0+(cx1-cx0)*t;
cy=cy0+(cy1-cy0)*t;
if(left){
left[2]=x0;
left[3]=y0;
left[4]=cx0;
left[5]=cy0;
left[6]=cx;
left[7]=cy;
}
if(right){
right[0]=cx;
right[1]=cy;
right[2]=cx1;
right[3]=cy1;
right[4]=x1;
right[5]=y1;
}
}
double *PathSegmentEndPoint(PATHSEGMENT*ps){
if(!ps)
return NULL;
switch (ps->type){
default:
return NULL;
case PATH_MOVETO:
case PATH_LINETO:
return &ps->coords[0];
case PATH_QUADTO:
return &ps->coords[2];
case PATH_CUBICTO:
return &ps->coords[4];
}
}
BOOL PathMoveTo(ITEMARRAY*ia,double x,double y){
PATHSEGMENT *ps=NULL;
if(!ISARRAY(ia,PATHSEGMENT))return FALSE;
ps=ItemArrayAllocOnePtr(ia);
if(!ps)return FALSE;
ps->type=PATH_MOVETO;
ps->coords[0]=x;
ps->coords[1]=y;
return TRUE;
}
BOOL PathAddPoint(ITEMARRAY *ia, double x, double y){
double endpoint[2];
BOOL haveendpoint;
if(!ISARRAY(ia,PATHSEGMENT))return FALSE;
haveendpoint=PathGetEndPoint(ia,endpoint);
if(!haveendpoint||endpoint[0]!=x||endpoint[1]!=y){
return PathMoveTo(ia,x,y);
}
return TRUE;
}
BOOL PathLineTo(ITEMARRAY*ia,double x,double y){
PATHSEGMENT *ps;
if(!ISARRAY(ia,PATHSEGMENT)||ia->length==0)return 0;
ps=ItemArrayAllocOnePtr(ia);
if(!ps)return 0;
ps->type=PATH_LINETO;
ps->coords[0]=x;
ps->coords[1]=y;
return TRUE;
}
BOOL PathQuadTo(ITEMARRAY*ia,double x,double y,double x1,double y1){
PATHSEGMENT *ps;
if(!ISARRAY(ia,PATHSEGMENT)||ia->length==0)return FALSE;
ps=ItemArrayAllocOnePtr(ia);
if(!ps)return FALSE;
ps->type=PATH_QUADTO;
ps->coords[0]=x;
ps->coords[1]=y;
ps->coords[2]=x1;
ps->coords[3]=y1;
return TRUE;
}
BOOL PathCubicTo(ITEMARRAY*ia,
double x,double y,double x1,double y1,double x2,double y2){
PATHSEGMENT *ps;
if(!ISARRAY(ia,PATHSEGMENT)||ia->length==0)return 0;
ps=ItemArrayAllocOnePtr(ia);
if(!ps)return FALSE;
ps->type=PATH_CUBICTO;
ps->coords[0]=x;
ps->coords[1]=y;
ps->coords[2]=x1;
ps->coords[3]=y1;
ps->coords[4]=x2;
ps->coords[5]=y2;
return TRUE;
}
BOOL PathSmoothQuadTo(ITEMARRAY *ia, double x, double y){
PATHSEGMENT *ps;
if(!ISARRAY(ia,PATHSEGMENT)||ia->length==0)return 0;
ps=ItemArrayPtr(ia,ia->length-1);
if(ps->type==PATH_QUADTO){
double newx=ps->coords[2]*2 - ps->coords[0];
double newy=ps->coords[3]*2 - ps->coords[1];
return PathQuadTo(ia,newx,newy,x,y);
} else {
double endpt[2];
if(!PathGetEndPoint(ia,endpt))
return FALSE;
return PathQuadTo(ia,endpt[0],endpt[1],x,y);
}
}
BOOL PathSmoothCubicTo(ITEMARRAY *ia, double x1, double y1, double x2, double y2){
PATHSEGMENT *ps;
if(!ISARRAY(ia,PATHSEGMENT)||ia->length==0)return 0;
ps=ItemArrayPtr(ia,ia->length-1);
if(ps->type==PATH_CUBICTO){
double newx=ps->coords[4]*2 - ps->coords[2];
double newy=ps->coords[5]*2 - ps->coords[3];
return PathCubicTo(ia,newx,newy,x1,y1,x2,y2);
} else {
double endpt[2];
if(!PathGetEndPoint(ia,endpt))
return FALSE;
return PathCubicTo(ia,endpt[0],endpt[1],x1,y1,x2,y2);
}
}
BOOL PathClose(ITEMARRAY*ia){
PATHSEGMENT *ps=NULL;
if(!ISARRAY(ia,PATHSEGMENT)||ia->length==0)return 0;
ps=ItemArrayPtr(ia,ia->length-1);
if(ps->type==PATH_CLOSE)
return TRUE;
ps=ItemArrayAllocOnePtr(ia);
if(!ps)return FALSE;
ps->type=PATH_CLOSE;
return TRUE;
}
BOOL PathAddSegment(ITEMARRAY*ia,PATHSEGMENT*ps){
if(!ISARRAY(ia,PATHSEGMENT)||!ps)
return FALSE;
switch (ps->type){
case PATH_MOVETO:
return PathMoveTo(ia,ps->coords[0],ps->coords[1]);
case PATH_CLOSE:
return PathClose(ia);
case PATH_LINETO:
return PathLineTo(ia,ps->coords[0],ps->coords[1]);
case PATH_QUADTO:
return PathQuadTo(ia,ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3]);
case PATH_CUBICTO:
return PathCubicTo(ia,ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3],ps->coords[4],ps->coords[5]);
default:
return FALSE;
}
}
BOOL PathAppendPath(
ITEMARRAY*ia,
PATHITERATOR *pcb,
LPVOID data,
MATRIX*xfm,
BOOL connect
){
PATHSEGMENT ps;
LPVOID lParam;
int i;
PATHSEGMENT *pps;
double *endpt;
if(!pcb||!ISARRAY(ia,PATHSEGMENT))
return FALSE;
lParam=pcb->Init(data);
if(!lParam)
return FALSE;
while(pcb->Next(lParam,&ps)>0){
PathSegmentTransform(&ps,xfm);
switch (ps.type){
case PATH_MOVETO:
if(!connect||ia->length==0){
PathMoveTo(ia,ps.coords[0],ps.coords[1]);
break;
}
pps=ItemArrayPtr(ia,ia->length-1);
endpt=PathSegmentEndPoint(pps);
if(endpt&&endpt[0]==ps.coords[0]&&endpt[1]==ps.coords[1]){
} else {
PathLineTo(ia,ps.coords[0],ps.coords[1]);
}
break;
case PATH_LINETO:
PathLineTo(ia,ps.coords[0],ps.coords[1]);
break;
case PATH_QUADTO:
PathQuadTo(ia,ps.coords[0],ps.coords[1],ps.coords[2],ps.coords[3]);
break;
case PATH_CUBICTO:
PathCubicTo(ia,ps.coords[0],ps.coords[1],
ps.coords[2],ps.coords[3],ps.coords[4],ps.coords[5]);
break;
case PATH_CLOSE:
PathClose(ia);
break;
}
connect=FALSE;
}
pcb->Free(lParam);
return TRUE;
}
void DebugOutFullPathSegment(FULLPATHSEGMENT*ps,LPCTSTR add){
#ifdef DEBUG
if(add){
DebugOutF(add);
DebugOutF(" ");
}
switch (ps->type){
case PATH_MOVETO:
case PATH_LINETO:
case PATH_CLOSE:
DebugOut("[%d] %f %f %f %f",ps->type,ps->coords[0],ps->coords[1],ps->coords[2],ps->coords[3]);
break;
case PATH_QUADTO:
DebugOut("[%d] %f %f %f %f %f %f",ps->type,ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3],ps->coords[4],ps->coords[5]);
break;
case PATH_CUBICTO:
DebugOut("[%d] %f %f %f %f %f %f %f %f",ps->type,ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3],ps->coords[4],ps->coords[5],ps->coords[6],ps->coords[7]);
break;
}
#endif
}
void DebugOutPathSegment(PATHSEGMENT*ps,LPCTSTR add){
#ifdef DEBUG
if(add){
DebugOutF(add);
DebugOutF(" ");
}
switch (ps->type){
case PATH_MOVETO:
case PATH_LINETO:
DebugOut("[%d] %f %f",ps->type,ps->coords[0],ps->coords[1]);
break;
case PATH_QUADTO:
DebugOut("[%d] %f %f %f %f",ps->type,ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3]);
break;
case PATH_CUBICTO:
DebugOut("[%d] %f %f %f %f %f %f",ps->type,ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3],ps->coords[4],ps->coords[5]);
break;
case PATH_CLOSE:
DebugOut("[%d]",ps->type);
break;
}
#endif
}
void DebugOutPathSegmentAsSvg(PATHSEGMENT *ps){
#ifdef DEBUG
switch(ps->type){
case PATH_CLOSE:
DebugOutF("Z ");
break;
case PATH_MOVETO:
DebugOutF("M%f,%f ",ps->coords[0],ps->coords[1]);
break;
case PATH_LINETO:
DebugOutF("L%f,%f ",ps->coords[0],ps->coords[1]);
break;
case PATH_QUADTO:
DebugOutF("Q%f,%f,%f,%f ",ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3]);
break;
case PATH_CUBICTO:
DebugOutF("C%f,%f,%f,%f,%f,%f ",ps->coords[0],ps->coords[1],
ps->coords[2],ps->coords[3],
ps->coords[4],ps->coords[5]);
break;
}
#endif
}
BOOL PathGetEndPoint(ITEMARRAY *ia,double *endpt){
LONG realtype;
PATHSEGMENT *ps;
LONG i;
if(!endpt||!ISARRAY(ia,PATHSEGMENT)||ia->length==0){
return FALSE;
}
i=ia->length-1;
ps=(PATHSEGMENT*)ItemArrayPtr(ia,i);
if(ps->type==PATH_CLOSE){
int j;
for(j=i;j>=0;j--){
ps=(PATHSEGMENT*)ItemArrayPtr(ia,j);
if(ps->type==PATH_MOVETO)
break;
}
if(j<0){
return FALSE;
}
}
switch(ps->type){
case PATH_MOVETO:
case PATH_LINETO:
endpt[0]=ps->coords[0];
endpt[1]=ps->coords[1];
break;
case PATH_QUADTO:
endpt[0]=ps->coords[2];
endpt[1]=ps->coords[3];
break;
case PATH_CUBICTO:
endpt[0]=ps->coords[4];
endpt[1]=ps->coords[5];
break;
default:
return FALSE;
}
return TRUE;
}
void PathCardinalSplineTo(
ITEMARRAY *path,
double *coords,
LONG numpoints,
double tension
){
PTDBL *pts=(PTDBL*)coords;
PTDBL point0;
LONG i;
double x0,x1,y0,y1;
if(numpoints<1)return;
if(!PathGetEndPoint(path,(double*)&point0))
return;
tension/=3;
for(i=0;i<numpoints;i++){
LONG iprev=i-1;
LONG ithispt=i;
LONG inext1=i+1;
LONG inext2=i+2;
PTDBL *prev,*thispt,*next1,*next2;
prev=(iprev==0) ? &point0 : &pts[iprev-1];
thispt=(ithispt==0) ? &point0 : &pts[ithispt-1];
next1=(inext1==0) ? &point0 : &pts[inext1-1];
next2=(inext2==0) ? &point0 : &pts[inext2-1];
if(iprev<0){
ASSERT(ithispt==0);
x0=thispt->x+(next1->x-thispt->x)*tension;
y0=thispt->y+(next1->y-thispt->y)*tension;
} else {
x0=thispt->x+(next1->x-prev->x)*tension;
y0=thispt->y+(next1->y-prev->y)*tension;
}
if(inext2>=numpoints+1){
x1=next1->x+(thispt->x-next1->x)*tension;
y1=next1->y+(thispt->y-next1->y)*tension;
} else {
x1=next1->x-(next2->x-thispt->x)*tension;
y1=next1->y-(next2->y-thispt->y)*tension;
}
PathCubicTo(path,x0,y0,x1,y1,next1->x,next1->y);
}
}
void PathAddCardinalSpline(
ITEMARRAY *path,
double *coords,
LONG numpoints,
double tension,
BOOL closefigure
){
PTDBL *pts=(PTDBL*)coords;
LONG i;
double x0,x1,y0,y1;
if(closefigure && numpoints<3)return;
if(numpoints<2)return;
tension/=3;
PathMoveTo(path,pts[0].x,pts[0].y);
for(i=0;i<numpoints;i++){
LONG prev=i-1;
LONG thispt=i;
LONG next1=i+1;
LONG next2=i+2;
if(thispt==0 && closefigure)
prev=numpoints-1;
if(thispt==numpoints-2 && closefigure)
next2=0;
if(thispt==numpoints-1){
if(!closefigure)break;
next1=0;
next2=1;
}
if(prev<0){
ASSERT(thispt==0);
x0=pts[thispt].x+(pts[next1].x-pts[thispt].x)*tension;
y0=pts[thispt].y+(pts[next1].y-pts[thispt].y)*tension;
} else {
x0=pts[thispt].x+(pts[next1].x-pts[prev].x)*tension;
y0=pts[thispt].y+(pts[next1].y-pts[prev].y)*tension;
}
if(next2>=numpoints){
x1=pts[next1].x+(pts[thispt].x-pts[next1].x)*tension;
y1=pts[next1].y+(pts[thispt].y-pts[next1].y)*tension;
} else {
x1=pts[next1].x-(pts[next2].x-pts[thispt].x)*tension;
y1=pts[next1].y-(pts[next2].y-pts[thispt].y)*tension;
}
PathCubicTo(path,x0,y0,x1,y1,pts[next1].x,pts[next1].y);
}
if(closefigure){
PathClose(path);
}
}
BOOL PathTransform(ITEMARRAY *path,MATRIX*xfm){
DWORD i;
if(!ISARRAY(path,PATHSEGMENT)||!xfm)return FALSE;
for(i=0;i<path->length;i++){
PATHSEGMENT *seg=ItemArrayPtr(path,i);
PathSegmentTransform(seg,xfm);
}
return TRUE;
}
BOOL PathSegmentTransform(PATHSEGMENT*ps,MATRIX*xfm){
if(!ps)
return FALSE;
if(!xfm)
return TRUE;
switch (ps->type){
case PATH_MOVETO:
case PATH_LINETO:
MatrixTransformPoints(xfm,ps->coords,1);
return TRUE;
case PATH_QUADTO:
MatrixTransformPoints(xfm,ps->coords,2);
return TRUE;
case PATH_CUBICTO:
MatrixTransformPoints(xfm,ps->coords,3);
return TRUE;
case PATH_CLOSE:
return TRUE;
default:
return FALSE;
}
}
typedef struct{
int stage;
double coords[8];
} SEGITERATOR;
static int SegIterator_GetWinding(LPVOID h){
return WIND_NONZERO;
}
static LPVOID SegIterator_Init(double *data, DWORD numpoints){
double *rect=(double*)data;
SEGITERATOR *iterator;
if(!data)return NULL;
iterator=malloc(sizeof(SEGITERATOR));
if(iterator){
DWORD i;
iterator->stage=0;
for(i=0;i<numpoints;i++){
iterator->coords[i]=data[i];
}
}
return iterator;
}
static void SegIterator_Free(LPVOID h){
free(h);
}
static LPVOID LineIterator_Init(double *data){
return SegIterator_Init(data,4);
}
static int LineIterator_Next(LPVOID h, PATHSEGMENT *ps){
SEGITERATOR *rc=(SEGITERATOR *)h;
if(!h||!ps)return -1;
switch (rc->stage){
case 0:
ps->type=PATH_MOVETO;
ps->coords[0]=rc->coords[0];
ps->coords[1]=rc->coords[1];
break;
case 1:
ps->type=PATH_LINETO;
ps->coords[0]=rc->coords[2];
ps->coords[1]=rc->coords[3];
break;
default:
return 0;
}
rc->stage++;
return 1;
}
static LPVOID QuadIterator_Init(double *data){
return SegIterator_Init(data,6);
}
static int QuadIterator_Next(LPVOID h, PATHSEGMENT *ps){
SEGITERATOR *rc=(SEGITERATOR *)h;
if(!h||!ps)return -1;
switch (rc->stage){
case 0:
ps->type=PATH_MOVETO;
ps->coords[0]=rc->coords[0];
ps->coords[1]=rc->coords[1];
break;
case 1:
ps->type=PATH_QUADTO;
ps->coords[0]=rc->coords[2];
ps->coords[1]=rc->coords[3];
ps->coords[2]=rc->coords[4];
ps->coords[3]=rc->coords[5];
break;
default:
return 0;
}
rc->stage++;
return 1;
}
static LPVOID CubicIterator_Init(double *data){
return SegIterator_Init(data,8);
}
static int CubicIterator_Next(LPVOID h, PATHSEGMENT *ps){
SEGITERATOR *rc=(SEGITERATOR *)h;
if(!h||!ps)return -1;
switch (rc->stage){
case 0:
ps->type=PATH_MOVETO;
ps->coords[0]=rc->coords[0];
ps->coords[1]=rc->coords[1];
break;
case 1:
ps->type=PATH_CUBICTO;
ps->coords[0]=rc->coords[2];
ps->coords[1]=rc->coords[3];
ps->coords[2]=rc->coords[4];
ps->coords[3]=rc->coords[5];
ps->coords[2]=rc->coords[6];
ps->coords[3]=rc->coords[7];
break;
default:
return 0;
}
rc->stage++;
return 1;
}
typedef struct{
int stage;
double rect[4];
} RECTITERATOR;
static int RectPath_GetWinding(LPVOID h){
return WIND_NONZERO;
}
static LPVOID RectPath_Init(LPVOID data){
double *rect=(double*)data;
RECTITERATOR *rectiter;
if(!data)return NULL;
rectiter=malloc(sizeof(RECTITERATOR));
if(rectiter){
rectiter->stage=0;
rectiter->rect[0]=rect[0];
rectiter->rect[1]=rect[1];
rectiter->rect[2]=rect[2];
rectiter->rect[3]=rect[3];
}
return rectiter;
}
static void RectPath_Free(LPVOID h){
free(h);
}
static int RectPath_Next(LPVOID h, PATHSEGMENT *ps){
RECTITERATOR *rc=(RECTITERATOR *)h;
if(!h||!ps)return -1;
switch (rc->stage){
case 0:
ps->type=PATH_MOVETO;
ps->coords[0]=rc->rect[0];
ps->coords[1]=rc->rect[1];
break;
case 1:
ps->type=PATH_LINETO;
ps->coords[0]=rc->rect[2];
ps->coords[1]=rc->rect[1];
break;
case 2:
ps->type=PATH_LINETO;
ps->coords[0]=rc->rect[2];
ps->coords[1]=rc->rect[3];
break;
case 3:
ps->type=PATH_LINETO;
ps->coords[0]=rc->rect[0];
ps->coords[1]=rc->rect[3];
break;
case 4:
ps->type=PATH_CLOSE;
break;
default:
return 0;
}
rc->stage++;
return 1;
}
typedef struct{
int stage;
RECTDBL rect;
} ELLIPSEITERATOR;
#define PCV 0.77614237491539666
#define NCV 0.22385762508460333
static double ctrlpts[4][6]={
{1.0,PCV,PCV,1.0,0.5,1.0},
{NCV,1.0,0.0,PCV,0.0,0.5},
{0.0,NCV,NCV,0.0,0.5,0.0},
{PCV,0.0,1.0,NCV,1.0,0.5}
};
static int EllipsePath_GetWinding(LPVOID h){
return WIND_NONZERO;
}
static LPVOID EllipsePath_Init(LPVOID data){
double *rect=(double*)data;
ELLIPSEITERATOR *rectiter;
if(!data)return NULL;
rectiter=malloc(sizeof(ELLIPSEITERATOR));
if(rectiter){
rectiter->stage=0;
rectiter->rect.left=rect[0];
rectiter->rect.top=rect[1];
rectiter->rect.right=rect[2];
rectiter->rect.bottom=rect[3];
}
return rectiter;
}
static void EllipsePath_Free(LPVOID h){
free(h);
}
static int EllipsePath_Next(LPVOID handle, PATHSEGMENT *ps){
ELLIPSEITERATOR *rc=(ELLIPSEITERATOR *)handle;
if(!handle||!ps)return -1;
double *ctrls;
double w,h;
if(rc->stage>=6)
return 0;
if(rc->stage==5){
ps->type=PATH_CLOSE;
rc->stage++;
return 1;
}
w=RectWidth(&rc->rect);
h=RectHeight(&rc->rect);
if(rc->stage==0){
ps->type=PATH_MOVETO;
ctrls=ctrlpts[3];
ps->coords[0]=rc->rect.left+ctrls[4]*w;
ps->coords[1]=rc->rect.top+ctrls[5]*h;
rc->stage++;
return 1;
}
ctrls=ctrlpts[rc->stage-1];
ps->type=PATH_CUBICTO;
ps->coords[0]=rc->rect.left+ctrls[0]*w;
ps->coords[1]=rc->rect.top+ctrls[1]*h;
ps->coords[2]=rc->rect.left+ctrls[2]*w;
ps->coords[3]=rc->rect.top+ctrls[3]*h;
ps->coords[4]=rc->rect.left+ctrls[4]*w;
ps->coords[5]=rc->rect.top+ctrls[5]*h;
rc->stage++;
return 1;
}
#define NCV 0.22385762508460333
static double rrctrlpts[]={
0.0,0.0,0.0,0.5,
0.0,0.0,1.0,-0.5,
0.0,0.0,1.0,-NCV,
0.0,NCV,1.0,0.0,
0.0,0.5,1.0,0.0,
1.0,-0.5,1.0,0.0,
1.0,-NCV,1.0,0.0,
1.0,0.0,1.0,-NCV,
1.0,0.0,1.0,-0.5,
1.0,0.0,0.0,0.5,
1.0,0.0,0.0,NCV,
1.0,-NCV,0.0,0.0,
1.0,-0.5,0.0,0.0,
0.0,0.5,0.0,0.0,
0.0,NCV,0.0,0.0,
0.0,0.0,0.0,NCV,
0.0,0.0,0.0,0.5,
};
static int rrindices[]={
0,4,8,20,24,36,40,52,56,68,68
};
static int rrtypes[]={
PATH_MOVETO,
PATH_LINETO,PATH_CUBICTO,
PATH_LINETO,PATH_CUBICTO,
PATH_LINETO,PATH_CUBICTO,
PATH_LINETO,PATH_CUBICTO,
PATH_CLOSE
};
typedef struct{
int stage;
ROUNDRECT rect;
} RRITERATOR;
static int RoundRectPath_GetWinding(LPVOID h){
return WIND_NONZERO;
}
static LPVOID RoundRectPath_Init(LPVOID data){
ROUNDRECT *rect=(ROUNDRECT *)data;
RRITERATOR *rectiter;
if(!data)return NULL;
rectiter=malloc(sizeof(RRITERATOR));
if(rectiter){
rectiter->stage=0;
rectiter->rect=*rect;
}
return rectiter;
}
static void RoundRectPath_Free(LPVOID h){
free(h);
}
static int RoundRectPath_Next(LPVOID handle, PATHSEGMENT *ps){
RRITERATOR *rc=(RRITERATOR *)handle;
ROUNDRECT *rrc;
double *ctrls;
double w,h,aw,ah;
int nc=0,i;
if(!handle||!ps)return -1;
rrc=&rc->rect;
if(rc->stage>=DIMOF(rrtypes))
return 0;
w=RectWidth(rrc);
h=RectHeight(rrc);
aw=min(w,rrc->cxArc);
ah=min(h,rrc->cyArc);
ps->type=rrtypes[rc->stage];
for(i=rrindices[rc->stage];i<rrindices[rc->stage+1];i+=4){
ps->coords[nc++]=(rrc->left+rrctrlpts[i+1]*aw+rrctrlpts[i]*w);
ps->coords[nc++]=(rrc->top+rrctrlpts[i+3]*ah+rrctrlpts[i+2]*h);
}
rc->stage++;
return 1;
}
PATHITERATOR RoundRectPath={
RoundRectPath_Init,
RoundRectPath_Next,
RoundRectPath_Free,
RoundRectPath_GetWinding
};
PATHITERATOR EllipsePath={
EllipsePath_Init,
EllipsePath_Next,
EllipsePath_Free,
EllipsePath_GetWinding
};
PATHITERATOR RectPath={
RectPath_Init,
RectPath_Next,
RectPath_Free,
RectPath_GetWinding
};
PATHITERATOR LinePath={
LineIterator_Init,
LineIterator_Next,
SegIterator_Free,
SegIterator_GetWinding
};
PATHITERATOR QuadPath={
QuadIterator_Init,