path2d.c

Go to the documentation of this file.

#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){ // if slice
   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){ //x
  case 0:
   x=0.0;
   break;
  case 1:
   x=0.5*(vcx-xw);
   break;
  case 2:
   x=(vcx-xw);
   break;
  }
  switch (align/3){ //y
  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){
   // At start of curve
   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){
   // At end of curve
   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){
   // At start of curve
   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){
   // At end of curve
   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;
 }
}

//     Rectangles
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;
  // Copy left, top, right, bottom of source rect
  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;
  // Copy left, top, right, bottom of source rect
  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;
  // Copy left, top, right, bottom of source rect
  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;
  // Copy left, top, right, bottom of source rect
  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,