/*********************************************************
*                                                        *
*                 program:      corral                   *
*          ==================================            *
*                                                        *
*       date: 19-JUN-1989     by: ANDREAS NEUPER         *
*                                                        *
*    compile: AZTEC_C3.4 +ff  link: -ls -lm -lc +Cb      *
*                                                        *
*                   release V1.205                       *
*                                                        *
*   suffix-files, headlines, arg-handling, auto-sizing   *
*                                                        *
*   file-request, centering, turning, 4gadget-handling   *
*                                                        *
**********************************************************
*                                                        *
*  BUGS:                                                 *
*                                                        *
*  LACK:                                                 *
*                                                        *
*********************************************************/
#include <stdio.h>
#include <math.h>
#include <time.h>
#include <graphics/gfxbase.h>
#include <graphics/rastport.h>
#include <graphics/display.h>
#include <intuition/intuition.h>

#define  dPI            6.2831853072
#define  MAX            2000
#define  sqr(x)         ((x)*(x))
#define  wp(x,y)        WritePixel(rp,(long)((x)<<1),(long)(y));WritePixel(rp,(long)(((x)<<1)+1),(long)(y))

int   Nummer,radius=30,offset=110;
int   Menge_x[MAX],Menge_y[MAX];
char  titles[90],buf[18];

struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
struct RastPort      *rp;
struct Window        *window,*strwindow;
struct IntuiMessage  *message;
struct Message       *GetMsg();
struct Gadget        *GadgetPtr;
ULONG  MessageClass;
USHORT code;
extern double ran();
extern double sqrt();
unsigned char undostr[100],outstr[100];

struct StringInfo string =
{  outstr,undostr,
   0,100,0,0,
   0,0,0,0,
   NULL,0,NULL
};

short sgp[] =
{
   0,0, 322,0, 322,12, 0,12, 0,0
};

struct Border sgb =
{
   -2,-2,1,0,JAM1,5,sgp,NULL
};

struct IntuiText sgt =
{
   3,0,JAM2,-42,1,NULL,(UBYTE *)"FILE",NULL
};

struct Gadget StrGadg =
{
   NULL,
   50,5,320,10,
   GADGHCOMP,
   RELVERIFY|STRINGCENTER,
   STRGADGET,
   (APTR)&sgb,
   NULL,
   &sgt,
   NULL,
   (APTR)&string,
   5,NULL
};

short gp[] =
{
   0,0, 11,0, 11,11, 0,11, 0,0
};

struct Border gb =
{
   -1,-1,1,0,JAM1,5,gp,NULL
};
struct IntuiText gtr =
{
   2,0,JAM2,1,1,NULL,(UBYTE *)"R",NULL
};

struct Gadget RotGadg =
{
   NULL,
   -10,-10,
   10, 10,
   GADGHCOMP|GRELRIGHT|GRELBOTTOM,
   RELVERIFY,
   BOOLGADGET,
   (APTR)&gb,
   NULL,
   &gtr,
   NULL,NULL,
   4,   NULL
};

struct IntuiText gtv =
{
   2,0,JAM2,1,1,NULL,(UBYTE *)"V",NULL
};

struct Gadget VerGadg =
{
   &RotGadg,
   -10, 1,
   10, 10,
   GADGHCOMP|GRELRIGHT,
   RELVERIFY,
   BOOLGADGET,
   (APTR)&gb,
   NULL,
   &gtv,
   NULL,NULL,
   3,   NULL
};

struct IntuiText gth =
{
   2,0,JAM2,1,1,NULL,(UBYTE *)"H",NULL
};

struct Gadget HorGadg =
{
   &VerGadg,
   1,-10,
   10,10,
   GADGHCOMP|GRELBOTTOM,
   RELVERIFY,
   BOOLGADGET,
   (APTR)&gb,
   NULL,
   &gth,
   NULL,NULL,
   2,   NULL
};

struct IntuiText gtc =
   {
   2,0,JAM2,1,1,NULL,(UBYTE *)"C",NULL
   };

struct Gadget CenterGadg =
{
   &HorGadg,
   1, 1,
   10,10,
   GADGHCOMP,
   RELVERIFY,
   BOOLGADGET,
   (APTR)&gb,
   NULL,
   &gtc,
   NULL,NULL,
   1,   NULL
};

struct NewWindow nw =
  {
   18, 8,    /* Ecken */
   100,60,/*Breit,Hoch*/
   3,  1,     /* Pens */
   CLOSEWINDOW|REFRESHWINDOW|GADGETUP,
   WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG|SUPER_BITMAP|GIMMEZEROZERO,
   &CenterGadg,
   NULL,     /* Gadget  Checkmark */
   (UBYTE *)"Spektrum Februar 1989",
   NULL,     /* Zeiger auf Screen */
   NULL,/* Zeiger auf SuperBitMap */
   40,  20,    /* Min. Breit,Hoch */
   480,240,    /* Max. Breit,Hoch */
   WBENCHSCREEN
  };

struct NewWindow nw2 =
  {
   20, 100,
   375,30,
   2,  1,
   CLOSEWINDOW|GADGETUP,
   WINDOWCLOSE|BORDERLESS|GIMMEZEROZERO|ACTIVATE,
   &StrGadg,
   NULL,
   (UBYTE *)"OUTPUT_filename:        (Spektrum Feb.1989)",
   NULL,
   NULL,
   20, 10,
   480,50,
   WBENCHSCREEN
  };

close_all()
{  extern void CloseLibrary();
   extern void CloseWindow();

   if (window)        CloseWindow(window);
   if (GfxBase)       CloseLibrary(GfxBase);
   if (IntuitionBase) CloseLibrary(IntuitionBase);
   exit(FALSE);
}

open_all()
{  void                    *OpenLibrary();
   extern struct  Window   *OpenWindow();

   if (!(GfxBase = (struct GfxBase *)
      OpenLibrary("graphics.library", (long)0)))
      close_all();

   if (!(IntuitionBase = (struct IntuitionBase *)
      OpenLibrary("intuition.library", (long)0)))
      close_all();

   if (!(window = (struct Window *)
      OpenWindow(&nw)))
      close_all();

   rp = window->RPort;
   SetDrMd(rp,JAM1);    /* Drawmode  setzen                  */
   SetAPen(rp,(long)3); /* Farbregister zum Zeichnen setzen  */
}

center()
{  register int i,sumx=0,sumy=0;
   int   maxrad=0,rad,hoch1=0,hoch2=0,breit1=0,breit2=0;

   for(i=0;i<Nummer;i++)
   {
      sumx += Menge_x[i];
      sumy += Menge_y[i];
   }
   sumx = -sumx/Nummer;
   sumy = -sumy/Nummer;
   for(i=0;i<Nummer;i++)
   {
      Menge_x[i] += sumx;
      Menge_y[i] += sumy;
      rad = sqr(Menge_x[i])+sqr(Menge_y[i]);
      if ( rad > maxrad ) maxrad = rad;
      if ( Menge_x[i] > breit2 ) breit2 = Menge_x[i];
      if ( Menge_y[i] > hoch2 ) hoch2 = Menge_y[i];
      if ( Menge_x[i] < breit1 ) breit1 = Menge_x[i];
      if ( Menge_y[i] < hoch1 ) hoch1 = Menge_y[i];
   }
   sprintf(titles,"max.  Radius: %.2f   Width: %3d   Height: %3d   Shift(x|y): (%2d |%2d )",
      sqrt((double)maxrad),(breit2-breit1),(hoch2-hoch1),sumx,sumy);
   SetWindowTitles(window,buf,titles);
}

rotate()
{  register int i,help;

   for(i=0;i<Nummer;i++)
   {  help = Menge_x[i];
      Menge_x[i] = Menge_y[i];
      Menge_y[i] = -help;
}  }

void  shift(shiftn)
int   shiftn;
{  register int   i;

   SetAPen(rp,(long)0);
   for(i=0;i<Nummer;i++) {wp(Menge_x[i]+offset,Menge_y[i]+offset);}
   SetAPen(rp,(long)3);
   if (shiftn>0)
   {  offset += shiftn;
      radius += shiftn;
   } else switch(shiftn)
   {
      case -1 : center();break;
      case -2 : for(i=0;i<Nummer;i++) Menge_x[i]=-Menge_x[i];break;
      case -3 : for(i=0;i<Nummer;i++) Menge_y[i]=-Menge_y[i];break;
      case -4 : rotate();break;
   }
   if(((offset<<1)+window->TopEdge<240)&&((offset<<2)+window->LeftEdge<475))
      SizeWindow(window,(long)((offset<<2)-window->Width),(long)((offset<<1)-window->Height+10));
   else {
      MoveWindow(window,-1*(long)(window->LeftEdge),-1*(long)(window->TopEdge));
      Delay(2L);
      if(((offset<<1)+window->TopEdge<240)&&((offset<<2)+window->LeftEdge<475))
         SizeWindow(window,(long)((offset<<2)-window->Width),(long)((offset<<1)-window->Height+10));
   }
   Delay(12L);
   for(i=0;i<Nummer;i++) {wp(Menge_x[i]+offset,Menge_y[i]+offset);}
   RefreshGadgets(&CenterGadg,window,NULL);
}

void eingabe(argc,arg)
int  argc;
char **arg;
{  FILE *infile;
   char fn[40];
   int k,position;
   short x,y;

   x = window->Width+window->LeftEdge;
   y = window->Height+window->TopEdge;
   if ( ( argc > 2 ) || ( ( argc == 2 ) && ( arg[1][0] == '?' ) ) )
      { fprintf(stderr,"\n  \033[7;30;43m\033[0;33;42m USAGE : \033[0;33m   corral [ workFILE ]\033[0;31m\n\n"); close_all(); exit(10); }
   if ( argc == 1 )
   {  Menge_x[0]=0;   Menge_y[0]=0;
      Nummer = 1;
      radius = 10;
      offset = 25;

      if (!(strwindow = (struct Window *)  OpenWindow(&nw2)))
      {
         if (strwindow) CloseWindow(strwindow);
         close_all();
      }
		ActivateGadget(&StrGadg,window,NULL);
      for(;;)
      if (message = (struct IntuiMessage *) GetMsg(strwindow->UserPort))
      {
         code = message->Code;
         MessageClass = message->Class;
         GadgetPtr = (struct Gadget *) message->IAddress;
         ReplyMsg(message);
         if ((MessageClass == GADGETUP)||(MessageClass == CLOSEWINDOW))
         {  if (strwindow) CloseWindow(strwindow);
            break;
      }  }
      infile = fopen(outstr,"r");
   } else infile = fopen(arg[1],"r");
   if ( infile == (long)0 )
   {
      Menge_x[0]=0;   Menge_y[0]=0;
      Nummer = 1;
      radius = 10;
      offset = 25;
   } else {
      fscanf(infile,"%d",&Nummer);
      for(k=0;k<Nummer;k++) fscanf(infile,"%d%d",&Menge_x[k],&Menge_y[k]);
      fclose(infile);
      radius = (int)(sqrt((double)Nummer)*2)+2;
      offset = radius+5;
      if (Nummer<75) offset=25;
      if (offset < 120) shift(0);
      else {fprintf(stderr,"Too many particles!\n");close_all();}
   }
   RefreshGadgets(&CenterGadg,window,NULL);
} 

void ausgabe(argc,argv)
int  argc;
char **argv;
{  FILE *outfile;
   char fn[100],*help1;
   unsigned char *help2;
   register int j,k;

   if ( argc < 2 )
   {
      help1=fn;help2=outstr; /*warning ok!*/
      while((*help1 = *help2++)&&(*help2 != '.'))help1++;
      *(help1+1) = '\0';
   } else {
      k=strlen(argv[1]);
      for(j=0;(j<k)&&(argv[1][j]!='.');j++) fn[j] = argv[1][j];
      fn[j] = (char)0;
   }
   fn[(k=strlen(fn))]='.';
   ftoa((double)Nummer,buf,0,1);
   for(j=0;j<6;j++) fn[j+k+1] = buf[j];
   outfile = fopen(fn,"w");

   if ( outfile == (long)0 ) { fprintf(stderr,"  \033[0;33mOutput-File cannot be opened by CORRAL !\033[0;31m\n\n"); close_all(); exit(5); }

   fprintf(outfile,"%d\n",Nummer);
   for(k=0;k<Nummer;k++) fprintf(outfile,"%d %d\n",Menge_x[k],Menge_y[k]);
   fclose(outfile);
}

double random()	/* nur etwas langsamer, dafür zufälliger */
{	long	timer,	*zeroo = 0;
   timer = time(zeroo);
   return((ran()*.9+(double)(timer%10)/(double)100));
}

void  corral(argc,argv)
int argc;
char **argv;
{  register int   i,x,y;
   register short Kontakt;
   int            x2,y2,Anzahl,Abstand;
   double         Wahl,Winkel;

   for(i=0;i<Nummer;i++) {wp(Menge_x[i]+offset,Menge_y[i]+offset);}
   Kontakt = 0;
   sprintf(titles,"CORRAL      -     a Fractal Tree                 ©JAN");
   while(radius<180)
   {
      Winkel = dPI*Wahl;
      x = radius*cos(Winkel);
      y = radius*sin(Winkel);

      x2 = y2 = Abstand = 0;/* für neues Teilchen NULL setzen */

      do {
         x2 = x;
         y2 = y;  /* x3 and y3  o n l y  necessary for s.a.w. */

         SetAPen(rp,(long)0);
         wp(x+offset,y+offset);
         do {
            Wahl = random();                      /* random walk */
            if ( Wahl < 0.25 ) --x;
            else {
               if ( Wahl < 0.5 ) x++;
               else {
                  if ( Wahl < 0.75 ) --y;
                  else y++;
            }  }
         }while((x == x2) && (y == y2));/* self avoiding walk */
         
         SetAPen(rp,(long)1);
         wp(x+offset,y+offset);
         Abstand = sqr(x)+sqr(y);

         for(i=0;i<Nummer;i++)
         {
            if( ( (x-1) == Menge_x[i] ) && ( y == Menge_y[i] ) ) Kontakt = 1;
            if( ( (x+1) == Menge_x[i] ) && ( y == Menge_y[i] ) ) Kontakt = 1;
            if( ( x == Menge_x[i] ) && ( (y-1) == Menge_y[i] ) ) Kontakt = 1;
            if( ( x == Menge_x[i] ) && ( (y+1) == Menge_y[i] ) ) Kontakt = 1;
         }   
      }while((Abstand < sqr(radius)) && (Kontakt == 0));
      if ( Kontakt == 0 )
      {  SetAPen(rp,(long)0);
         wp(x+offset,y+offset);
      } else {
         Menge_x[Nummer] = x;
         Menge_y[Nummer++] = y;
         Kontakt = 0;
         if (Abstand > (sqr(radius)-3)) shift(5);
         sprintf(buf,"%d Particles",Nummer);
         SetWindowTitles(window,buf,titles);
         if (Nummer == (Nummer/100)*100) ausgabe(argc,argv);
      }
      if (message = (struct IntuiMessage *) GetMsg(window->UserPort))
      {  code = message->Code;
         MessageClass = message->Class;
         GadgetPtr = (struct Gadget *) message->IAddress;
         ReplyMsg(message);
         if (MessageClass == GADGETUP) shift(-(GadgetPtr->GadgetID));
         if (MessageClass == CLOSEWINDOW) return();
}  }  }

main(argc,argv)
int argc;
char **argv;
{
   open_all();
   eingabe(argc,argv);
   corral(argc,argv);
   ausgabe(argc,argv);
   center();Delay(100L);
   close_all();
}