/* verctorop.c vectorization routine for stscan.c */
#include <exec/exec.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <graphics/rastport.h>
#include <libraries/reqbase.h>
#include <req_pragmas.h>
#include <stdio.h>
#include <string.h>
#include <functions.h>
#include "stscan.h"

#define REGION_SIZE 64
#define CHAINLEN 16384

static UWORD chpixels[2][CHAINLEN];
static UWORD lnpixels[2][CHAINLEN];
static UWORD regio_x[128][128];
static UWORD regio_y[128][128];
static UBYTE regio_done[128][128];
static UBYTE regio_numx,regio_numy;
static char str_filenam[REQ_FCHARS+REQ_DSIZE]="";
static UWORD maxdiff=4;

static UBYTE getpixel(UWORD x,UWORD y)
{ 
  return((*(memptr+(ULONG)y*(ULONG)membpl+(ULONG)(x>>3))&bitval[x&7])?0:1);
}

static void clrpixel(UWORD x,UWORD y)
{ *(memptr+(ULONG)y*(ULONG)membpl+(ULONG)(x>>3))|=bitval[x&7];
}

static void setpixel(UWORD x,UWORD y)
{ *(memptr+(ULONG)y*(ULONG)membpl+(ULONG)(x>>3))&=invbitval[x&7];
}

static UBYTE subiter(UWORD nx,UWORD ny)
{ UWORD x,y;
  UBYTE f,vw,vnw,vn,vne,ve,vse,vs,vsw;

  f=0;
  for (y=regio_y[nx][ny];y<regio_y[nx][ny+1];y++)
  { for (x=regio_x[nx][ny];x<regio_x[nx+1][ny];x++)
    { if ((getpixel(x,y))&&((x&1)==(y&1)))
      { vw= getpixel(x-1,y);
        vnw=getpixel(x-1,y-1);
        vn= getpixel(x  ,y-1);
        vne=getpixel(x+1,y-1);
        ve= getpixel(x+1,y);
        vse=getpixel(x+1,y+1);
        vs= getpixel(x  ,y+1);
        vsw=getpixel(x-1,y+1);
        if ( (( ((vw^1)&(vnw|vn))
               +((vn^1)&(vne|ve))
               +((ve^1)&(vse|vs))
               +((vs^1)&(vsw|vw)) )==1)
            || ((!(vw))&&(!(vnw))&&(!(vn))&&(!(vne))&&
                (!(ve))&&(!(vse))&&(!(vs))&&(!(vsw)) ) )
        { clrpixel(x,y);
          if (!(f)) f=((vnw|vse)&(vne|vsw)&(vn|vs)&(vw|ve));
        }
      }
    }
  }
  for (y=regio_y[nx][ny];y<regio_y[nx][ny+1];y++)
  { for (x=regio_x[nx][ny];x<regio_x[nx+1][ny];x++)
    { if ((getpixel(x,y))&&((x&1)!=(y&1)))
      { vw= getpixel(x-1,y);
        vnw=getpixel(x-1,y-1);
        vn= getpixel(x  ,y-1);
        vne=getpixel(x+1,y-1);
        ve= getpixel(x+1,y);
        vse=getpixel(x+1,y+1);
        vs= getpixel(x  ,y+1);
        vsw=getpixel(x-1,y+1);
        if ( (( ((vw^1)&(vnw|vn))
               +((vn^1)&(vne|ve))
               +((ve^1)&(vse|vs))
               +((vs^1)&(vsw|vw)) )==1)
            || ((!(vw))&&(!(vnw))&&(!(vn))&&(!(vne))&&
                (!(ve))&&(!(vse))&&(!(vs))&&(!(vsw)) ) )
        { clrpixel(x,y);
          if (!(f)) f=((vnw|vse)&(vne|vsw)&(vn|vs)&(vw|ve));
        }
      }
    }
  }
  return(f);
}

static UBYTE chkblack(UWORD nx,UWORD ny)
{ UWORD x,y;
  UBYTE f=1;
  for (y=regio_y[nx][ny];y<regio_y[nx][ny+1];y++)
  { for (x=regio_x[nx][ny];x<regio_x[nx+1][ny];x++)
    if (!(getpixel(x,y))) 
    { f=0;
      y=regio_y[nx][ny+1];
      x=regio_x[nx+1][ny];
    }
  }
  return(f);
}

static void straighten()
{ UWORD x,y;
  UBYTE f,vnw,vne,vse,vsw;

  for (y=1;y<memheight-1;y++)
  { for (x=1;x<memwidth-1;x++)
    { if ((getpixel(x,y))
          &&(!(getpixel(x-1,y)))&&(!(getpixel(x+1,y)))
          &&(!(getpixel(x,y-1)))&&(!(getpixel(x,y+1))))
      { vnw=getpixel(x-1,y-1);
        vne=getpixel(x+1,y-1);
        vse=getpixel(x+1,y+1);
        vsw=getpixel(x-1,y+1);
        if (((vnw))&&(!(vne))&&(!(vse))&&((vsw))) 
        { clrpixel(x,y);
          setpixel(x-1,y);
        }
        else if ((!(vnw))&&((vne))&&((vse))&&(!(vsw))) 
        { clrpixel(x,y);
          setpixel(x+1,y);
        }
        else if (((vnw))&&((vne))&&(!(vse))&&(!(vsw))) 
        { clrpixel(x,y);
          setpixel(x,y-1);
        }
        else if ((!(vnw))&&(!(vne))&&((vse))&&((vsw))) 
        { clrpixel(x,y);
          setpixel(x,y+1);
        }
      }
    }
  }
}

void thin()
{ UWORD x,y,i;
  ULONG pgofs1,pgofs2;
  UBYTE p,f,ende;
  char wtitel[80];
  
  membpl=(memwidth>>3);
  pgofs1=(memwidth>>3)*(memheight-1);
  for (i=0;i<(memwidth>>3);i++) 
  { *(memptr+i)=255;
    *(memptr+pgofs1+i)=255;
  }
  pgofs1=0;
  pgofs2=membpl-1;
  for (i=0;i<memheight;i++)
  { *(memptr+pgofs1)|=128;
    *(memptr+pgofs2)|=1;
    pgofs1+=membpl;
    pgofs2+=membpl;
  }
  p=1;
  regio_numx=memwidth/REGION_SIZE;
  regio_numy=memheight/REGION_SIZE;
  regio_x[0][0]=1;
  regio_x[1][0]=REGION_SIZE+(memwidth%REGION_SIZE)/2;
  for (x=2;x<regio_numx;x++) regio_x[x][0]=REGION_SIZE+regio_x[x-1][0];
  regio_x[regio_numx][0]=memwidth-1;
  for (x=0;x<regio_numx;x++) regio_y[x][0]=1;
  for (x=0;x<=regio_numx;x++)
  { regio_y[x][1]=REGION_SIZE+(memheight%REGION_SIZE)/2;
    regio_x[x][1]=regio_x[x][0];
  }
  for (y=2;y<regio_numy;y++) for (x=0;x<=regio_numx;x++)
  { regio_y[x][y]=REGION_SIZE+regio_y[x][y-1];
    regio_x[x][y]=regio_x[x][0];
  }
  for (x=0;x<regio_numx;x++) regio_y[x][regio_numy]=memheight-1;
  for (y=0;y<regio_numy;y++) for (x=0;x<regio_numx;x++) regio_done[x][y]=1;
  do
  { ende=0;
    for (y=0;y<regio_numy;y++) for (x=0;x<regio_numx;x++)
    { if (regio_done[x][y])
      { if (subiter(x,y)) f=regio_done[x][y]=1;
        else if (chkblack(x,y)) f=regio_done[x][y]=1;
        else f=regio_done[x][y]=0;
        ende|=f;
        sprintf(&wtitel,"Region (%02d;%02d) Pass %02d",x,y,p);
        SetWindowTitles(win,&wtitel[0],(UBYTE *)-1);
      }
    }
    p++;
  } while (ende);
  SetWindowTitles(win,"Cleanup",(UBYTE *)-1);
  straighten(); 
  SetWindowTitles(win," ",(UBYTE *)-1);
}

static UWORD getchain(UWORD x, UWORD y)
{ UWORD lx,ly,pos;
  UBYTE f=1;

  lx=x;
  ly=y;
  pos=0;
  while ((f)&&(pos<CHAINLEN))
  { chpixels[0][pos]=lx;
    chpixels[1][pos]=ly;
    clrpixel(lx,ly);
    pos++;
         if (getpixel(lx-1,ly  )) lx--;
    else if (getpixel(lx-1,ly-1)) {lx--; ly--;}
    else if (getpixel(lx  ,ly-1)) ly--;
    else if (getpixel(lx+1,ly-1)) {lx++; ly--;}
    else if (getpixel(lx+1,ly  )) lx++;
    else if (getpixel(lx+1,ly+1)) {lx++; ly++;}
    else if (getpixel(lx  ,ly+1)) ly++;
    else if (getpixel(lx-1,ly+1)) {lx--; ly++;}
    else f=0;
  }
  return(pos);
}

static UWORD getline(UWORD x0, UWORD y0, UWORD x1, UWORD y1)
{ WORD xdiff,ydiff,xstep,ystep, sum;
  UWORD i;
  WORD step[2]={-1,1};

  xstep=step[(x0<x1)]; ystep=step[(y0<y1)];
  xdiff=abs(x0-x1); ydiff=abs(y0-y1);

  lnpixels[0][0]=x0;
  lnpixels[1][0]=y0;
  i=1;
  if (xdiff>ydiff)
  { sum=xdiff/2;
    while (x0!=x1)
    { x0+=xstep;
      sum-=ydiff;
      if (sum<0)
      { y0+=ystep;
        sum+=xdiff;
      }
      lnpixels[0][i]=x0;
      lnpixels[1][i]=y0;
      i++;
    }
  }
  else
  { sum=ydiff/2;
    while (y0!=y1)
    { y0+=ystep;
      sum-=xdiff;
      if (sum<0)
      { x0+=xstep;
        sum+=ydiff;
      }
      lnpixels[0][i]=x0;
      lnpixels[1][i]=y0;
      i++;
    }
  }
  return(i);
}

static void polygon(UWORD startpos,UWORD len,FILE *vecfile)
{ UWORD sx0,sy0,sx1,sy1,m,n,s,lnlen,cbdist,mcbdist,mdpos;
  
  sx0=chpixels[0][startpos];
  sy0=chpixels[1][startpos];
  sx1=chpixels[0][startpos+len-1];
  sy1=chpixels[1][startpos+len-1];
  lnlen=getline(sx0,sy0,sx1,sy1);
  mdpos=0;
  mcbdist=0;
  s=len/lnlen;
  if (s<1) s=1;
  for(m=0,n=startpos;m<lnlen;m++,n+=s)
  { cbdist=abs(chpixels[0][n]-lnpixels[0][m])
          +abs(chpixels[1][n]-lnpixels[1][m]);
    if (cbdist>mcbdist)
    { mdpos=m;
      mcbdist=cbdist;
    }
  }
  if (mcbdist>maxdiff)
  { polygon(startpos,mdpos,vecfile);
    polygon(startpos+mdpos,len-mdpos,vecfile);
  }
  else
  { fprintf(vecfile,"%d %d\n",sx0,memheight-sy0);
    fprintf(vecfile,"%d %d\n\n",sx1,memheight-sy1);
  }
}

void vectorize()
{ UWORD p,x,y,len;
  FILE *vecfile;
  char wtitel[80];
  
  if (filerequest("Save Vector TXT File",str_filenam))
  { p=0;
    membpl=(memwidth>>3);
    vecfile=fopen(str_filenam,"w");
    for (y=1;y<memheight-1;y++)
    { for (x=1;x<memwidth-1;x++)
      { if (getpixel(x,y))
        { len=getchain(x,y);
          polygon(0,len,vecfile);
          sprintf(&wtitel,"Chain %04d",++p);
          SetWindowTitles(win,&wtitel[0],(UBYTE *)-1);
        }
      }
    }
    fclose(vecfile);
    SetWindowTitles(win," ",(UBYTE *)-1);
  }
}

void chngmaxdiff()
{ struct Process *OurTask;
  struct Window	*old_pr_WindowPtr;
  struct GetLongStruct	gnum;

  OurTask = (struct Process *)FindTask((char *)0L);
  old_pr_WindowPtr = OurTask->pr_WindowPtr;
  OurTask->pr_WindowPtr = win;
  memset(&gnum,0,sizeof(gnum));
  gnum.titlebar = "Enter vectorization accuracy.";
  gnum.defaultval = maxdiff;
  gnum.minlimit = 2;
  gnum.maxlimit = 100;
  if (GetLong(&gnum)) maxdiff=gnum.result;
  OurTask->pr_WindowPtr = old_pr_WindowPtr;
}
