/*->c.fit */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


#include "h.os"
#include "h.wimp"
#include "h.sprite"
#include "h.wimpt"
#include "h.bbc"
#include "h.akbd"
#include "h.werr"
#include "h.flex"
#include "h.DrawLevel0"


#include "h.wos"
#include "h.DrawLib"
#include "h.mytrace"
#include "h.pic"
#include "h.err"
#include "h.simplex"

#include "h.fit"



typedef struct bezier
{
 ipoint2 left;
 ipoint2 leftc;
 ipoint2 rightc;
 ipoint2 right;

} bezier;



extern ipoint2 * xgencurv(bezier * p,ipoint2 * vector,ipoint2 * veclim);
extern int xresid(int point,int npoint,ipoint2 * vec,ipoint2 * vector);
extern int xsetscale(int x,int y);


ipoint2 * vector;
int       point;
int       maxvpoint;

#define   VPSIZE 8192 


double    pixerror=0.5;     /* acceptable error in fraction of pixel */
int       pixlimit;



/* given initial bezier curve, and somewhere to put the points */

#ifdef NEVER

void gencurv(bezier * p)
{
 bezier a;
 bezier b;
 int    temp;


 if(
(abs(p->left.x-p->right.x)<(256*spdx)) && (abs(p->left.y-p->right.y)<(256*spdy)) &&
(abs(p->leftc.x-p->rightc.x)<(256*spdx)) && (abs(p->leftc.y-p->rightc.y)<(256*spdy))
   )
 {
  /* points close enough */
  /* output points       */

  if(point<maxvpoint)
  {
   vector[point++]=p->left;
   vector[point++]=p->right;
  }
  else
  {
   if(flex_extend((flex_ptr)&vector,maxvpoint*sizeof(ipoint2)+VPSIZE))
                                            maxvpoint+=VPSIZE/sizeof(ipoint2);
   if(point<maxvpoint)
   {
    vector[point++]=p->left;
    vector[point++]=p->right;
   }
  }
 }
 else
 {
  /* both */

  a.left=p->left;
  b.right=p->right;

  /* x coords */

  temp=(p->leftc.x+p->rightc.x)/2;

  a.leftc.x =(p->left.x+p->leftc.x)/2;
  a.rightc.x=(temp+a.leftc.x)/2;
  b.rightc.x=(p->rightc.x+p->right.x)/2;
  b.leftc.x =(temp+b.rightc.x)/2;

  a.right.x=(a.rightc.x+b.leftc.x)/2;

  /* y coords */

  temp=(p->leftc.y+p->rightc.y)/2;

  a.leftc.y =(p->left.y+p->leftc.y)/2;
  a.rightc.y=(temp+a.leftc.y)/2;
  b.rightc.y=(p->rightc.y+p->right.y)/2;
  b.leftc.y =(temp+b.rightc.y)/2;

  a.right.y=(a.rightc.y+b.leftc.y)/2;

  b.left=a.right;

  gencurv(&a);
  gencurv(&b);
 }
}

#endif






os_error * gencurv(bezier * p)
{
 bezier   stack[100];
 bezier   a;
 register int    temp;
 int      sp;
 os_error * err;

 sp=1;
 a=*p;
 err=NULL;

 while(sp)
 {

  if(
(abs(a.left.x-a.right.x)<(spddx)) && (abs(a.left.y-a.right.y)<(spddy)) &&
(abs(a.leftc.x-a.rightc.x)<(spddx)) && (abs(a.leftc.y-a.rightc.y)<(spddy))
   )
 {
  /* points close enough */
  /* output points       */

  if(point<maxvpoint)
  {
   vector[point++]=a.left;
   vector[point++]=a.right;
  }
  else
  {
   err=flex_extende((flex_ptr)&vector,maxvpoint*sizeof(ipoint2)+VPSIZE);
   if(err) break;
   maxvpoint+=VPSIZE/sizeof(ipoint2);
   if(point<maxvpoint)
   {
    vector[point++]=a.left;
    vector[point++]=a.right;
   }
  }
  a=stack[sp-1];
  sp--;
 }
 else
 {
  /* both */

  p=&stack[sp];

  p->right.x=a.right.x;
  p->right.y=a.right.y;

  /* x coords */

  temp=(a.leftc.x+a.rightc.x)>>1;

  p->rightc.x=(a.rightc.x+a.right.x)>>1;
  p->leftc.x =(temp+p->rightc.x)>>1;

  a.leftc.x =(a.left.x+a.leftc.x)>>1;
  a.rightc.x=(temp+a.leftc.x)>>1;

  p->left.x=a.right.x=(a.rightc.x+p->leftc.x)>>1;

  /* y coords */

  temp=(a.leftc.y+a.rightc.y)>>1;

  p->rightc.y=(a.rightc.y+a.right.y)>>1;
  p->leftc.y =(temp+p->rightc.y)>>1;

  a.leftc.y =(a.left.y+a.leftc.y)>>1;
  a.rightc.y=(temp+a.leftc.y)>>1;

  p->left.y=a.right.y=(a.rightc.y+p->leftc.y)>>1;

  sp++;
  }
 }

 return(err);
}









int rescount;
int restime;


int maxx;
int maxy;
int minx;
int miny;


/* input is vector of points, bezier curve         */
/* output error measure, plus point with max error */

                                                            
os_error *residuals(ipoint2 * vec,int npoints,bezier * b,int * maxr,int * sumr)
{
 int i;
 int j;
 int sum;
 register int temp;
 int diff1;
 int maxi;
 int max;
 int jbig;
 os_error * err;

 rescount++;
 err=NULL;

 while(1)
 {
  point=xgencurv(b,vector,vector+maxvpoint)-vector;
  if(point>=maxvpoint)
  {
   err=flex_extende((flex_ptr)&vector,maxvpoint*sizeof(ipoint2)+VPSIZE);
   if(err) break;
   maxvpoint+=VPSIZE/sizeof(ipoint2);
  }
  else break;
 }

 if(!err)
 {
  maxx=minx=vector[0].x;
  maxy=miny=vector[0].y;

  for(j=0;j<point;j++)
  {
   if(maxx<vector[j].x) maxx=vector[j].x;
   if(minx>vector[j].x) minx=vector[j].x;
   if(maxy<vector[j].y) maxy=vector[j].y;
   if(miny>vector[j].y) miny=vector[j].y;
  }


  sum=0;
  max=0;
  j=jbig=0;


  for(i=0;i<npoints;i++)
  {
   jbig+=point;
   while(jbig>npoints)
   {
    j++;
    jbig-=npoints;
   }

   temp=(vec[i].x-vector[j].x)/64; 
   diff1=temp*temp;

   temp=(vec[i].y-vector[j].y)/64; 
   diff1+=temp*temp;

   if(diff1>max)
   {
    maxi=i;
    max=diff1;
   }

   sum+=diff1;
   if(sum<0) break;
  }
 }

 *maxr=maxi;
 *sumr=sum;
 return(err);
}






                                                            
os_error * fastresiduals(ipoint2 * vec,int npoints,bezier * b,int * sumr)
{
 int sum;
 os_error * err;

 err=NULL;

 rescount++;

 while(1)
 {
  point=xgencurv(b,vector,vector+maxvpoint)-vector;
  if(point>=maxvpoint)
  {
   err=flex_extende((flex_ptr)&vector,maxvpoint*sizeof(ipoint2)+VPSIZE);
   if(err) break;
   maxvpoint+=VPSIZE/sizeof(ipoint2);
  }
  else break;
 }

 if(!err)
 {
  sum=xresid(point,npoints,vec,vector);
  *sumr=sum;
 }

 return(err);
}













ipoint2 * svector;  /* the data vector        */
int       spoints;  /* number of points in it */
bezier    sb;       /* bezier curve           */

int       op1;
int       op2;
int       op3;
int       op4;


void fxoutput(int p1,int p2,int p3,int p4)
{
 op1=p1;
 op2=p2;
 op3=p3;
 op4=p4;
}



os_error * sresiduals(int p1,int p2,int p3,int p4,int * sumr)
{
 bezier     b;
 int        sum;
 os_error * err;

 b=sb;
 b.leftc.x =p1;
 b.leftc.y =p2;
 b.rightc.x=p3;
 b.rightc.y=p4;

 err=fastresiduals(svector,spoints,&b,&sum);
 *sumr=sum;
 return(err);
}




os_error * mresiduals(int p1,int p2,int p3,int p4,int * max,int * sumr)
{
 bezier     b;
 int        sum;
 os_error * err;

 b=sb;
 b.leftc.x =p1;
 b.leftc.y =p2;
 b.rightc.x=p3;
 b.rightc.y=p4;

 err=residuals(svector,spoints,&b,max,&sum);
 *sumr=sum;
 return(err);
}




/* the entry point, passed a vector of npoints 2D points */
/* output a series of Beziers                            */


/* we call simplex                                                           */
/* we pass the values of the two variables                                   */
/* we set up global structures that residuals will use when simplex calls it */



os_error * fitcurvesub2(int vec,int npoints)
{
 int sum;
 int max;
 int code1;
 os_error * err;

 svector=pointstore+vec;
 spoints=npoints;

 sb.left =svector[0];
 sb.right=svector[npoints-1];

 op1=svector[npoints/3].x;
 op2=svector[npoints/3].y;
 op3=svector[(2*npoints)/3].x;
 op4=svector[(2*npoints)/3].y;

 err=simplex(op1,op2,op3,op4,&code1); 
 /* if(code1) */ if(!err) err=simplex(op1,op2,op3,op4,&sum);

 if(!err) err=mresiduals(op1,op2,op3,op4,&max,&sum);

 /* assume mresiduals called first        */
 /* sets up max and min x and y for curve */
 if(!err)
 {
  if((sum>=0 && sum<(npoints*pixlimit)) || npoints<=4) 
  {
   veclimits(maxx,maxy);
   veclimits(minx,miny);
  
   err=veccurve(op1,op2,op3,op4,sb.right.x,sb.right.y,&lastdiag);
  }
  else
  {
   if(max<4 || ((npoints-max)<4) || sum<0) max=npoints/2;

            err=fitcurvesub2(vec,max);
   if(!err) err=fitcurvesub2(vec+max,npoints-max);
  }
 }
 return(err);
}



#define ATDIM 9


int atab[ATDIM][ATDIM];

void initatab(void)
{
 int x;
 int y;

 for(x=0;x<ATDIM;x++)
  for(y=0;y<ATDIM;y++) 
  {
   if(x==y) atab[x][y]=45;
   else
   if(x==0) atab[x][y]=90;
   else     atab[x][y]=(int)((atan(((double)y)/((double)x))*45)/atan(1));
  }
}





int arctan(int dx,int dy)
{
 int a;

 a=atab[abs(dx)/256][abs(dy)/256];

 if(dx<0)
 {
  if(dy>0) a=180-a;
  else     a=180+a;
 }
 else
 {
  if(dy<0) a=360-a;
 }

 return(a);
}




os_error * fitcurvesub(int vec,int npoints,int * angle)
{
 os_error * err;
 int i;

 err=NULL;

 for(i=1;i<(npoints-1);i++) if(angle[i]) break;

 if(i<(npoints-1)) err=fitcurvesub2(vec,npoints);
 else              err=vecdraw(pointstore[vec+npoints-1].x,
                           pointstore[vec+npoints-1].y,&lastdiag);

 return(err);
}




os_error * fitcurve(int vec,int npoints)
{
 int * angle1;
 int * angle2;

 int i;
 int j;
 int next;
 int prev;
 int dx;
 int dy;
 int a;
 int start;
 os_error * err;


 rescount=0;
 restime=clock();

 xsetscale(spdx,spdy); 


 pixlimit=(int)(pixerror*pixerror*(double)((spddx*spddx+spddy*spddy)/(64*64)));

 err=flex_alloce((flex_ptr)&vector,VPSIZE);
 if(!err)
 {
  maxvpoint=VPSIZE/sizeof(ipoint2)-2;

  err=flex_alloce((flex_ptr)&angle1,npoints*sizeof(int));
  if(!err)
  {
   err=flex_alloce((flex_ptr)&angle2,npoints*sizeof(int));
   if(!err)
   {
    err=vecmove(pointstore[0].x,pointstore[0].y,&lastdiag);
    if(!err)
    {
     /* calculate angle turned at each point */
 
     for(i=0,next=1,prev=npoints-2;i<(npoints-1);i++)
     {
      dx=pointstore[prev].x-pointstore[i].x;
      dy=pointstore[prev].y-pointstore[i].y;
  
      angle1[i]=arctan(dx,dy);

      dx=pointstore[next].x-pointstore[i].x;
      dy=pointstore[next].y-pointstore[i].y;
  
      a=arctan(dx,dy);

      a=angle1[i]-a-180;
      if(a>180)  a=360-a;
      if(a<-180) a=a+360;
      angle1[i]=a;

      next++;
      if(next>=(npoints-1)) next=0;
      prev++;
      if(prev>=(npoints-1)) prev=0;
     }


 
     for(i=0,next=2,prev=npoints-3;i<(npoints-1);i++)
     {
      dx=pointstore[prev].x-pointstore[i].x;
      dy=pointstore[prev].y-pointstore[i].y;
  
      angle2[i]=arctan(dx,dy);
    
      dx=pointstore[next].x-pointstore[i].x;
      dy=pointstore[next].y-pointstore[i].y;
  
      a=arctan(dx,dy);

      a=angle2[i]-a-180;
      if(a>180)  a=360-a;
      if(a<-180) a=a+360;
      angle2[i]=a;

      next++;
      if(next>=(npoints-1)) next=0;
      prev++;
      if(prev>=(npoints-1)) prev=0;
     }



     for(i=0,start=0;i<npoints;i++)
     {
      if(angle2[i]>=90 || angle2[i]<=-90)
      {
       if((npoints-i)<4) i=npoints-1;
       if((i-start)>=4)
       {
        err=fitcurvesub(vec+start,i-start+1,angle1+start);
        if(err) break;
        start=i;
       }
      } 
     } 

     if(!err)
     {
      if((i-start)>=4) err=fitcurvesub(vec+start,i-start,angle1+start);
      else             
      {
       for(j=0;j<(i-start);j++)
       {
        err=vecdraw(pointstore[start+j].x,
                                    pointstore[start+j].y,&lastdiag);
        if(err) break;
       }
      }
     }
    }
    flex_free((flex_ptr)&angle2);
   }
   flex_free((flex_ptr)&angle1);
  }
  flex_free((flex_ptr)&vector);
 }

/* dprintf(0,"rescount=%d restime=%d rate=%d",rescount,clock()-restime,(100*rescount)/(clock()-restime));     */

 return(err);
}




void fitinit(void)
{
 initatab();



}



void savechoices(void)
{
 FILE * fp;

 fp=fopen("<Trace$Path>.Choices","wb");
 if(fp)
 {
  fwrite(&pixerror,sizeof(pixerror),1,fp);

  fclose(fp);
 }
}


void loadchoices(void)
{
 FILE * fp;

 fp=fopen("<Trace$Path>.Choices","rb");
 if(fp)
 {
  fread(&pixerror,sizeof(pixerror),1,fp);

  fclose(fp);
 }

}




void dotest(void)
{

 initatab();



}





