/*
*
*     IFSout.c - Iterated Function System
*                uses IFS to create a IFS-Construction Image
*     Released to the Public Domain - 1989 The Software Glen Company
*	
*
*/

#include "standard.h"
#include "ifs.h"
#include "ifsout.h"
#include "safeclose.h"

long   timeint;
short  gadcount;
float  x=0., y=0.;            /* the (real) x y before translation*/
UWORD  colortab[32]; 
int    currentfun = 0;
int    numoffun = 1;          /* startout with two functions=numfun+1 */
char   fname[MAXFNLEN];
char   fn[MAXFNLEN/2];
char   fdir[MAXFNLEN/2] = ":coors";
short   vectorsw = FALSE, 
        displaynumsw = TRUE, 
        pausesw = FALSE,
        blackbacksw = TRUE,
        adjopen = FALSE;

USHORT mx, my;				/* mouse location */

/*****************
** Main Program **
*****************/

int main()
{
  ULONG  class;
  UWORD  tempcolor;
  USHORT MenuNum, ItemNum, code;
  struct MenuItem *Item;   
  APTR   aAddress;
	   
  short  corner=0, adjdown = FALSE, boxmoved = FALSE;
  
  void   graphit();
  void   gadgetmessage();
  void   initialit();
  void   clearscreen();
  void   setcolor();
  void   setupgad();
  void   closeout();
  void   drawvectors();
  void   autoadj();
  int    closeby();
  void   adjust();
  void   displaynums();
  void   getifsfun();
  void   checkswitches();

  void   putifsfun();

/* default to small flower */
  float funs[FUNLIMIT][6] = { {0.20, 1.00, -0.65, 0.82, 1.11, 0.00},
                              {0.40, 0.00, 0.30, -0.32, 0.32, 0.00}},
        percent[FUNLIMIT] = { 0.66, 1.00}, 
        xyscale = 0.11;                       /* could be .25;     */ 

  int   xoff = WIDTH/2 - 24, 
        yoff = HEIGHT/2 + 28,
        i, j;

  initialit(funs,percent,&xyscale,&xoff,&yoff,&currentfun);
   
  FOREVER {
  
   if (pausesw) Wait((1 << w->UserPort->mp_SigBit) | 
		     (1 << adjwin->UserPort->mp_SigBit));
    if ((message = (struct IntuiMessage *)GetMsg(w->UserPort))) {
      class = message->Class;
      code  = message->Code;
      mx    = message->MouseX;
      my    = message->MouseY;

      aAddress = message->IAddress;
         
      ReplyMsg(message);       /* Can't reply until done using it!   */
      switch (class) {
        case  CLOSEWINDOW:   /* Exit the program */
          if (adjopen) 
            CloseWindowSafely(adjwin);
          closeout();
          exit(0);
          break;
        case MOUSEBUTTONS:
          if (code == SELECTDOWN) {
            if (vectorsw) {
              if (corner=closeby (funs, &currentfun, numoffun, mx, my) )
                adjdown = TRUE;
              else 
                if (numoffun > 0) currentfun = (currentfun + 1) % 
                                               (numoffun   + 1);
            }
            else setcolor();
          }
          else 
            if (code == SELECTUP) {
              adjdown = FALSE;
	      if (boxmoved) {
                clearscreen();
                boxmoved = FALSE;
                for (j=0;j<10;j++)           /* settle in on function */
                    graphit(funs,percent, xyscale, xoff, yoff, FALSE); 
                }
              checkswitches(rp,funs,percent,currentfun,
                           numoffun,vectorsw,displaynumsw);
	    }
          setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                    xoff, yoff, currentfun, numoffun, stxt, adjopen);
          break;               
        case INTUITICKS:
          if (adjdown) {
            boxmoved = TRUE;
            adjust(corner, rp, funs, currentfun, mx, my);
          }
          break;
        case MENUPICK:
          while (code != MENUNULL) {
            Item = (struct MenuItem *) ItemAddress(&Menu[0], code);
            MenuNum = MENUNUM( code ); 
            ItemNum = ITEMNUM( code );
            switch( MenuNum) {
              case 0:                  /* Project Menu  */
                switch( ItemNum ) {
                  case 0:              /* About Menu    */
                    AutoRequest(w, &ReqText1, NULL,
                      &OKIText, 10, 20, 310, 180);
                    break;
                  case 1:              /* Save Current  */
                    if (get_fname(w,screen,"Save File",fn,fdir) 
                      != NULL) {
                      fname[0] = '\0';
                      strcat(fname,fdir);

/* need to check if we need a / or not */
                      if (fdir[strlen(fdir) - 1] != ':')
                        strcat(fname,"/");

                      strcat(fname,fn);
                      putifsfun(fname,numoffun,funs,percent,
                                colortab,xoff,yoff,xyscale);
                      }
                    checkswitches(rp,funs,percent,currentfun,
                                  numoffun,vectorsw,displaynumsw);
                    break;
                  case 2:              /* Get IFS       */
                    if (get_fname(w,screen,"Get File",fn,fdir) != NULL) {
                      fname[0] = '\0';
                      strcat(fname,fdir);

/* need to check if we need a / or not */
                      if (fdir[strlen(fdir) - 1] != ':')
                        strcat(fname,"/");
                      strcat(fname,fn);
                      getifsfun(fname,&numoffun,funs, percent,
                                colortab,&xoff,&yoff,&xyscale);
                              
                      currentfun = 0;
                      PInfos[0].HorizBody = FFFF/(numoffun+1);
                      setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                              xoff, yoff, currentfun, numoffun, stxt, adjopen);
                      for (j=0;j<10;j++)           /* settle in on function */
                        graphit(funs, percent, xyscale, xoff, yoff, FALSE); 
                      clearscreen();
                      checkswitches(rp,funs,percent,currentfun,
                                    numoffun,vectorsw,displaynumsw);
                    }
                    break;                
                  case 3:              /* To WB         */
                    ScreenToBack(screen);
                    break;
                  case 4:              /* quit          */
                    if (adjopen) 
                      CloseWindowSafely(adjwin);
                    closeout();
                    exit(0);
                    break;
                }
                break;
              case 1:                  /* Function Menu */
                switch( ItemNum ) {
                  case 0:              /* Zoom to Fit   */
                    x=0.; y=0.;			
                    autoadj(funs, percent, &xyscale, &xoff, &yoff, &currentfun);
                    clearscreen();
                    checkswitches(rp,funs,percent,currentfun,
                                  numoffun,vectorsw,displaynumsw);
                    break;
                  case 1:              /* Zoom out x 2   */
                    xyscale = xyscale / 2;
                    clearscreen();
                    checkswitches(rp,funs,percent,currentfun,
                                  numoffun,vectorsw,displaynumsw);
                    setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                            xoff, yoff, currentfun, numoffun, stxt, adjopen);
                    break;
                  case 2:         /* Open adjust window in our new screen */
                    if (!adjopen) {
                      nadjwin.Screen = screen; 
                      adjwin = (struct Window *)OpenWindow(&nadjwin);
                      if (adjwin == NULL) {
                        closeout();
                        exit(6);
                      }
                      adjopen = TRUE;
                      setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                              xoff, yoff,currentfun, numoffun, stxt, adjopen);
                    }
                    else {
                      WindowToFront(adjwin);
                    }
                    break;
                  case 3:              /* Another Function */
                    if (numoffun < FUNLIMIT-1) {
                      numoffun++;
                      currentfun = numoffun;   
                      /* reset the new function */
                      if(displaynumsw) 
                        clearscreen();
                      funs[currentfun][0] = 1.;
                      funs[currentfun][1] = 0.;
                      funs[currentfun][2] = 0.;
                      funs[currentfun][3] = 1.;
                      funs[currentfun][4] = 0.;
                      funs[currentfun][5] = 0.;
                      adjpercent(numoffun, percent);
                      setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                              xoff, yoff,  currentfun, numoffun, stxt, adjopen);
                      checkswitches(rp,funs,percent,currentfun,
                                    numoffun,vectorsw,displaynumsw);
                    }
                    break; 
                  case 4:               /* Erase Current */
                    if(numoffun > 1) {
                      for (i=currentfun;i<numoffun;i++) 
                        for (j=0;j<6;j++) 
                         funs[i][j] = funs[i+1][j];
                      funs[numoffun][0] = 1.;
                      funs[numoffun][1] = 0.;
                      funs[numoffun][2] = 0.;
                      funs[numoffun][3] = 1.;
                      funs[numoffun][4] = 0.;
                      funs[numoffun][5] = 0.;
                      percent[numoffun] = 0.;
                      if (currentfun == numoffun) currentfun--;
                      numoffun--;
                      setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                              xoff, yoff,  currentfun, numoffun, stxt, adjopen);
                      clearscreen();
                      checkswitches(rp,funs,percent,currentfun,
                                    numoffun,vectorsw,displaynumsw);
                    }
                    break;
                  case 5:               /* Erase All     */     
                    numoffun = 1;       /* Leave two     */
                    currentfun = 0;
                    for (i=0;i<FUNLIMIT;i++)  {
                      funs[i][0] = 1.;
                      funs[i][1] = 0.;
                      funs[i][2] = 0.;
                      funs[i][3] = 1.;
                      funs[i][4] = 0.;
                      funs[i][5] = 0.;
                      percent[i] = 0.;
                    }
                    percent[0]=0.5;           /* for the two left */
                    percent[1]=1.;
                    setpots(adjwin, gads, PInfos, funs, percent, xyscale, 
                            xoff, yoff,  currentfun, numoffun, stxt, adjopen);
                    clearscreen();
                    checkswitches(rp,funs,percent,currentfun,
                                  numoffun,vectorsw,displaynumsw);
                    break;
                }   /* end of inner switch */

              break;
            case 2:			/*  Switches     */
              switch( ItemNum ) {
                case 0: 		/*  Toggle Boxes */
                  if (vectorsw) {
                    vectorsw = FALSE;
                    clearscreen();
                  }
                  else {
                    vectorsw = TRUE;
                  }
                  checkswitches(rp,funs,percent,currentfun,
                                numoffun,vectorsw,displaynumsw);
                  break;

                case 1:		/* Toggle Numbers */
                  if (displaynumsw) {
                    displaynumsw = FALSE;
                    clearscreen();
                    if (vectorsw) 
                      drawvectors(rp, funs, numoffun, currentfun);
                  }
                  else {
                    displaynumsw = TRUE;
                    displaynums(rp, funs, percent, currentfun, numoffun);
                  }
                  break;
        
                case 2:               /* Pause*/
                  if (pausesw) {
                    pausesw = FALSE;
                    ModifyIDCMP(w,CLOSEWINDOW|MOUSEBUTTONS|MENUPICK|INTUITICKS);
                    if(adjopen)
                      ModifyIDCMP(adjwin,CLOSEWINDOW|GADGETUP);
                  }
                  else {
                    pausesw = TRUE;   /* set to only look at menupicks/close */
                    ModifyIDCMP(w,CLOSEWINDOW|MENUPICK);
                    if(adjopen)
                      ModifyIDCMP(adjwin,CLOSEWINDOW);
                  }
                  break;
                case 3:               /* Toggle Bk/Fg  */
                  if (blackbacksw) {
                    blackbacksw = FALSE;
                  }
                  else {
                    blackbacksw = TRUE;
                  }
                  tempcolor   = colortab[0];
                  colortab[0] = colortab[1];
                  colortab[1] = tempcolor;
                  LoadRGB4(ViewPortAddress(w), colortab, 16);
                  break;
              } /* end of inner switch */
            }   /* end of outer switch */
          code = Item->NextSelect;
          }   /* end of if */
          break;  
      } /*  end of outer outer switch */
    } /* end of while */

/*  lets see if you want me to do something to adjwin		*/

  if (adjopen) 
    if((message=(struct IntuiMessage *)GetMsg(adjwin->UserPort))) {
      class = message->Class;
      code  = message->Code;
      aAddress = message->IAddress;
      ReplyMsg(message);      
      switch (class) {
        case  CLOSEWINDOW:		/*  Get rid of the requester	 */
          if (adjopen){
            CloseWindow(adjwin);
            adjopen = FALSE;
          }
          break;
        case GADGETUP:       /*reply, then process */
          gadgetmessage(aAddress,adjwin,funs,percent,&xyscale,&xoff,&yoff,
                        gads,PInfos,&numoffun,&currentfun,stxt,
                        displaynumsw,adjopen);
          for (j=0;j<10;j++)           /* settle in on function         */
            graphit(funs, percent, xyscale, xoff, yoff, FALSE); 
          checkswitches(rp,funs,percent,currentfun,
                        numoffun,vectorsw,displaynumsw);
          break;            
      } /* end switch */
    }  /* end if */
  if(!adjdown) 
    graphit(funs, percent, xyscale, xoff, yoff, TRUE); 
  }   /* end FOREVER */   
  return(0);
} /* end main */

void clearscreen() {

   SetAPen(rp,0);
   RectFill(rp, XSTART, YSTART-HEIGHT, WIDTH-XSTART, YSTART);
}

void graphit(funs,percent,xyscale,xoff,yoff,drawit)

float funs[][6],percent[],xyscale;
int   xoff,yoff,drawit;

{
  double pk;
  int i, ix, iy, p;
  static int color;
  float newx, newy;

  p = rand();
               
  pk = p/(float) INT_MAX;

  i=0;

  while(i<numoffun && pk > percent[i]) {
        i++;
     }

  newx = funs[i][0] * x + funs[i][1] * y + funs[i][4];
  newy = funs[i][2] * x + funs[i][3] * y + funs[i][5];

  x = newx;
  y = newy;

  ix = x*HEIGHT*xyscale+xoff;			/* Assumes that H<W      */
  iy = y*HEIGHT*xyscale+yoff;

  if ((color = (i+2)%MAXCOLORS) < 2) 
    color = color+2;
  if (drawit && ix>0 && ix<WIDTH && iy>0 && iy<HEIGHT) {
    SetAPen(rp, color);
    WritePixel(rp, ix, HEIGHT-iy);
  }
}

void initialit (funs,percent,xyscale,xoff,yoff,currentfun)

float funs[][6],percent[],*xyscale;
int   *xoff,*yoff,*currentfun;

{
   int i;

   srand(time(&timeint));		/* Set Random Generator Seed    */
                                        /* From Time                    */
   colortab[0] = 0;
   colortab[1] = 4095;

   GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0);
   if (GfxBase == NULL)
      exit(2);

   IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 0);
   if (IntuitionBase == NULL) {
      CloseLibrary(GfxBase);
      exit(3);
   }


   screen = (struct Screen *)OpenScreen(&ns); 
   if (screen == NULL) {
      CloseLibrary(IntuitionBase);
      CloseLibrary(GfxBase);
      exit(4);
      }

   nw.Screen = screen;                /* Open window in our new screen */
   w = (struct Window *)OpenWindow(&nw);
   if (w == NULL) {
      CloseScreen(screen);
      CloseLibrary(IntuitionBase);
      CloseLibrary(GfxBase);
      exit(5);
      }
    
   setupgad();
  
   for (i=2;i<FUNLIMIT;i++)  {
      funs[i][0] = 1.;
      funs[i][1] = 0.;
      funs[i][2] = 0.;
      funs[i][3] = 1.;
      funs[i][4] = 0.;
      funs[i][5] = 0.;
      percent[i] = 0.5;
      }

   setpots(adjwin, gads, PInfos, funs, percent, *xyscale, *xoff, *yoff,
           *currentfun, numoffun, stxt, adjopen);

   SetMenuStrip(w, &Menu[0]);
   ShowTitle(screen, FALSE);           /* No Bars in Arizona           */

   vp = &screen->ViewPort;             /* Set colors in screen's VP    */
   rp = w->RPort;                      /* Render into the window's RP  */
   clearscreen();

/* check and write indicators */ 
   checkswitches(rp,funs,percent,currentfun,numoffun,vectorsw,displaynumsw);

/*  Set the color registers */
   setcolor();

/* default color for small flower */
   colortab[2] = 1404;
   colortab[3] = 3913;
   LoadRGB4(ViewPortAddress(w), colortab, 16);
} /* end initialit */

void setcolor()  {

   int i, colorinc;

   colorinc = rand() % 4096;
   
   colortab[2] = rand() % 4096;


   for (i=3; i<32; i++) 
      colortab[i] = (colortab[i-1] + colorinc) % 4096;

   LoadRGB4(ViewPortAddress(w), colortab, 16);

   SetBPen(rp, 0);                    /* Insure clean text              */

}

void setupgad() {

  /* This is where the proportional gadgets are set up, using
   * the templates that were declared staticly.
  */
  short i, 
        gadcount;                     /* index to next available Gadget */

  for(gadcount = 0; gadcount < NUMPROPS-2; gadcount++) {
    gads[gadcount] = TPropGadget;
    PInfos[gadcount] = TPropInfo;
    gads[gadcount].GadgetText = NULL;       /* no text on prop gads */
    gads[gadcount].GadgetRender = (APTR)
    &PImages[gadcount];
    gads[gadcount].SpecialInfo = (APTR)&PInfos[gadcount];
    gads[gadcount].TopEdge = GADSIZE * (gadcount+2);
    if(gadcount != 0)
    gads[gadcount].NextGadget = &gads[gadcount-1];
  }   /* end for */

  gads[NUMPROPS-2] = ZoomGadget;
  PInfos[NUMPROPS-2] = TPropInfo;
  PInfos[NUMPROPS-2].Flags = AUTOKNOB | FREEVERT;
  gads[NUMPROPS-2].SpecialInfo = (APTR)&PInfos[NUMPROPS-2];
  gads[NUMPROPS-2].GadgetRender = (APTR) &PImages[NUMPROPS-2];
  gads[NUMPROPS-2].NextGadget = &gads[NUMPROPS-3];

  gads[NUMPROPS-1] = CenterGadget;
  PInfos[NUMPROPS-1] = TPropInfo;
  PInfos[NUMPROPS-1].Flags = AUTOKNOB | FREEVERT | FREEHORIZ;
  gads[NUMPROPS-1].SpecialInfo = (APTR)&PInfos[NUMPROPS-1];
  gads[NUMPROPS-1].GadgetRender = (APTR) &PImages[NUMPROPS-1];
  gads[NUMPROPS-1].NextGadget = &gads[NUMPROPS-2];
  
  PInfos[0].HorizBody = FFFF/(numoffun+1);

  /* This is where the String gadgets are set up, using
     the templates that were declared staticly.
  */

  for(i=0; i<NUMSTRS; i++) {
    SInfo[i].Buffer     = &stxt[i][0];
    SInfo[i].UndoBuffer = NULL;
    SInfo[i].BufferPos  = 0;
    SInfo[i].MaxChars   = GSTRLEN;
    SInfo[i].DispPos    = 0;
    }

  for(gadcount = NUMPROPS; gadcount < NUMGADS; gadcount++) {
    gads[gadcount] = TStrngGadget;
    gads[gadcount].GadgetText = 
      (struct IntuiText *) &StrngText[gadcount-NUMPROPS];
    gads[gadcount].GadgetRender = NULL;
    gads[gadcount].SpecialInfo = (APTR)&SInfo[gadcount-NUMPROPS];

    if(gadcount==NUMPROPS)
      gads[gadcount].TopEdge = 20;
    else 
      gads[gadcount].TopEdge = GADSIZE * (gadcount+2-NUMPROPS);
      gads[gadcount].NextGadget = &gads[gadcount-1];
   }   /* end for */
} /* end setupgad */

void closeout() {

   ClearMenuStrip(w);
   CloseWindow(w);
   CloseScreen(screen);
   CloseLibrary(IntuitionBase);
   CloseLibrary(GfxBase);

}

void displaynums(rp, funs, percent, currentfun, numoffun)
  struct   RastPort      *rp;
  float funs[][6], percent[];
  int currentfun, numoffun;
{
  short i, j, k, color;
  char str[80];

  j = HEIGHT - (8*numoffun) - 10;
  Move(rp,0,j);
   
  for (i=0; i<=numoffun; i++) {
    if ((color = (i+2)%MAXCOLORS) < 2) 
      color = color+2;
    SetAPen(rp, color);

    sprintf (str, "%3d ", i+1);
    Text(rp,str,strlen(str));
    for(k=0; k<6; k++) {         
      sprintf (str, "%5.2f ", funs[i][k]);
      Text(rp,str,strlen(str));
    }
    sprintf (str, "%5.2f", ((i>0) ? percent[i]-percent[i-1] : percent[i]));
    Text(rp,str,strlen(str));
    j = j+8;
    Move(rp,0,j);
   }
}

void autoadj(funs, percent, xyscale, xoff, yoff, currentfun) 

float funs[][6],percent[],*xyscale;
int   *xoff,*yoff,*currentfun;
{
int j;
float minx = 1e20;   
float miny = 1e20;  
float maxx = -minx;
float maxy = -minx;

for (j=0;j<100;j++) 
   {graphit(funs, percent, *xyscale, *xoff, *yoff, FALSE); 
    if (x < minx) minx = x;
    if (x > maxx) maxx = x;
    if (y < miny) miny = y;
    if (y > maxy) maxy = y;
    }

  if (maxx > minx && maxy > miny && minx > -1e20 && maxx < 1e20
    && miny > -1e20 && maxy < 1e20) {    /* don't scale if either diff = 0 */

    if ( (maxx-minx)/WIDTH > (maxy-miny)/HEIGHT) {
      *xyscale = (SCRNSCALE*ASPECT)/ (maxx - minx);
      *xoff = (WIDTH>>1)  * (1 - (SCRNSCALE*(maxx+minx)/(maxx-minx))); 
      *yoff = (HEIGHT>>1) * (1 - (*xyscale * (maxy+miny)));
    }
    else {
      *xyscale = SCRNSCALE / (maxy - miny);
      *xoff = ((WIDTH - HEIGHT * *xyscale * (maxx-minx))/2.0) - (minx * HEIGHT * *xyscale);
      *yoff = (HEIGHT>>1) * (1 - SCRNSCALE - (2.0 * miny * *xyscale)); 
    }

    setpots(adjwin, gads, PInfos, funs, percent, *xyscale, *xoff, *yoff,
            *currentfun, numoffun, stxt, adjopen);

    }
}	/* end autoadj */


void getifsfun(fname,numoffun,funs,percent,colortab,xoff,yoff,xyscale)
   char fname[MAXFNLEN];
   int *numoffun;
   float funs[][6], percent[];
   UWORD  colortab[]; 
   int *xoff,*yoff;
   float *xyscale;
{
   FILE *fp;

   int tempcolor;
   int i, j;
  
   if ((fp = fopen(fname, "r")) == NULL) {
      printf("ifsout:  can't open %s\n", fname);
   }
   else {
      fscanf(fp, "%d %d %d %f\n", numoffun, xoff, yoff, xyscale);
      for(i=0;i <= *numoffun;i++) {
        for(j=0; j<6; j++)       
          fscanf(fp, "%f ", &funs[i][j]);
        fscanf(fp, "%f %d\n", &percent[i], &tempcolor);
        colortab[i+2] = (UWORD) tempcolor; /* assumes that i<= FUNLIMIT */
      }
      fclose(fp);
      LoadRGB4(ViewPortAddress(w), colortab, 16);
   }
}

void putifsfun(fname,numoffun,funs,percent,colortab,xoff,yoff,xyscale)
  char fname[MAXFNLEN];
  int numoffun;
  float funs[][6], percent[];
  UWORD  colortab[]; 
  int xoff,yoff;
  float xyscale;
{
  FILE *fp;

  int i,j;
  
  if ((fp = fopen(fname, "w")) == NULL) {
    printf("ifsout:  can't open %s for write\n", fname);
   }
  else {
  fprintf(fp, "%d %d %d %f\n", numoffun, xoff, yoff, xyscale);
  for(i=0;i<=numoffun;i++) {
    for(j=0; j<6; j++)       
      fprintf(fp, "%5.2f ", funs[i][j]);
     /* assumes that FUNLIMIT <= 29 */
    fprintf(fp, " %5.2f %d\n", percent[i], colortab[i+2]);
   }
  fclose(fp);
  }
}



