#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <math.h>
/***********************************************************************
*  Superhop.c
*
*  This program opens a screen (using the XOpenScreen function),
*  then requests four numbers as input.  The numbers are used to 
*  generate a pattern as discussed in the August, 1986 Scientific 
*  American article on computer recreations.  After a pattern is
*  drawn, a portion of the pattern may be enlarged by selecting a
*  box with the mouse.  To start a new pattern, change one or more
*  of the input numbers.  To stop the program,
*  select the close window gadget.  
*
*  Note: The XOpenScreen function is described in Compute!'s Amiga
*  Programmer's Guide, and is not included here.  You'll have to 
*  link with your own screen open function.
*     Also, the inner-most loop uses the Motorola Fast Floating Point
*  routines, which are difficult to use from Lattice.  The MyInit routine
*  uses the usual C syntax, so if you want to compile with Manx, model 
*  the inner-most loop after the MyInit formulae.
*************************************************************************/
#define DOWN  1
#define UP    0

struct NewWindow newwindow = {
     0,   0,
   640, 200,
     0,   1,
   CLOSEWINDOW | MOUSEMOVE | MOUSEBUTTONS,
   WINDOWSIZING | SIZEBRIGHT | WINDOWDEPTH | WINDOWDRAG
      | SMART_REFRESH | ACTIVATE | NOCAREREFRESH
      | WINDOWCLOSE | REPORTMOUSE,    
   NULL,
   NULL,
   "Pattern Window",
   NULL,
   NULL,
   180, 50, 640, 200,
   CUSTOMSCREEN
};

struct Window *window, *g_window;
struct Screen *screen;
struct GfxBase *GfxBase;   /* note: the name MUST be GfxBase */
struct IntuitionBase *IntuitionBase;  /* as above */
struct IntuiMessage *message;
struct RastPort *DrawRP;
struct RastPort *GadgRP;
struct ViewPort *DrawVP;
struct Screen *XOpenScreen();

BYTE scaled = 0;

void add_gadgets();

/* for Manx Aztec C */
#if	MCH_AMIGA
#include <functions.h>
#endif

int MathBase;
int MathTransBase;

typedef union { float f; int i; } FFP;

float SPTieee();
char bmath[] = "mathffp.library";
char tmath[] = "mathtrans.library";

char input[40] = "-3.14,.3,.3,10000";
char outtext[40];

main()
{
   FFP affp, bffp, cffp, xscffp, xoffp, yoffp, f1ffp, xffp, yffp, xx, yy;
   BYTE gadgflag = 1;     /* identifies when gadget window is on */
   int color = 2;         /* drawing color */
   int Depth = 4;         /* number of bit planes in screen */
   int class, mcode;
   FLOAT a, b, c;         /* parameters used in pattern algorithm */
   FLOAT savea = 0, saveb = 0, savec = 0;  /* variables for saving a,b,c */
   FLOAT f1 = 0.44;       /* scaling factor */
   int i, num, dif;
   int x0 = 0, y0 = 0;    /* location of dot to be drawn, in pixels */
   FLOAT x = 0, y = 0;    /* location of dot to be drawn, in parameter space units */
   FLOAT xoff = 0, yoff = 0, xscale = 0;  /* scale parameters */
   FLOAT xmin, xmax, ymax, ymin;
   int tlcx, tlcy;        /* top left corner of box */
   int brcx, brcy;        /* bottom right corner of box */
   int boxx, boxy = 0;    /* corners of box while being drawn */
   BYTE boxon = 0;        /* flag for when a box is being drawn */ 
  
/* Open Libraries */ 
   if ((IntuitionBase =
         (struct IntuitionBase *)OpenLibrary("intuition.library",1)) == NULL)
      Cleanup("Error: couldn't open intuition library\n");
   if ((GfxBase =
         (struct GfxBase *)OpenLibrary("graphics.library",1)) == NULL)
      Cleanup("Error: couldn't open graphics library\n");
   if ((MathBase = OpenLibrary(bmath,0)) == NULL) 
      Cleanup("Error: couldn't open math library\n");
   if ((MathTransBase = OpenLibrary(tmath,0)) == NULL) 
      Cleanup("Error: couldn't open math transform library\n");

   if ((screen =
         (struct Screen *)XOpenScreen("Simple Screen", HIRES, Depth)) == NULL)
      Cleanup("Error: couldn't open custom screen\n");
   newwindow.Screen = screen;
   if ((window = (struct Window *)OpenWindow(&newwindow)) == NULL)
      Cleanup("Error: couldn't open window\n");
   DrawRP = window->RPort;
   DrawVP = &screen->ViewPort;
reset:
   gadgflag = 1;
   add_gadgets(screen);
/*   setcolors(DrawVP,colors); (A function to replace all this...) */
   SetRGB4(DrawVP, 0, 0, 0, 0);
   SetRGB4(DrawVP, 1, 15, 15, 15);
   SetRGB4(DrawVP, 2, 15, 0, 0);
   SetRGB4(DrawVP, 3, 0, 15, 0);
   SetRGB4(DrawVP, 4, 14, 3, 0);
   SetRGB4(DrawVP, 5, 15, 11, 0);
   SetRGB4(DrawVP, 6, 15, 15, 2);
   SetRGB4(DrawVP, 7, 15, 15, 15);
   SetRGB4(DrawVP, 8, 7, 13, 15);
   SetRGB4(DrawVP, 9, 11, 15, 0);
   SetRGB4(DrawVP, 10, 5, 13, 0);
   SetRGB4(DrawVP, 11, 12, 0, 14);
   SetRGB4(DrawVP, 12, 15, 2, 14);
   SetRGB4(DrawVP, 13, 15, 4, 10);
   SetRGB4(DrawVP, 14, 15, 6, 6);
   SetRGB4(DrawVP, 15, 15, 8, 2);
   SetAPen(DrawRP, color);
   SetDrMd(DrawRP, JAM2);

/* Is the gadget window showing? */

   while (gadgflag) {
/* Wait for something to happen. */
      Wait((1<<window->UserPort->mp_SigBit) | 
      		(1<<g_window->UserPort->mp_SigBit));
      while (message = (struct IntuiMessage *)GetMsg(window->UserPort)) {
/* Something happened in the main pattern window. */
         class = message->Class;
         switch (class) {
            case CLOSEWINDOW:         /* We are all done */
               ReplyMsg(message);
               Cleanup(NULL);
            case MOUSEBUTTONS:
               mcode = message->Code;
	       if(mcode == SELECTDOWN) { /* The user wants to set the TLC */
	          if(xoff == 0) break;
                  tlcx = message->MouseX;
                  tlcy = message->MouseY;
                  boxon = 1;
                  boxx = -1;
                  SetDrMd(DrawRP,COMPLEMENT);
               }
               else if(mcode == SELECTUP) { /* The user has defined the box */
                  Move(DrawRP,tlcx,tlcy);   /* redraw (erase) old box */
                  Draw(DrawRP,tlcx,boxy);
                  Draw(DrawRP,boxx,boxy);
                  Draw(DrawRP,boxx,tlcy);
                  Draw(DrawRP,tlcx,tlcy);
                  brcx = message->MouseX;
                  brcy = message->MouseY;
                  SetDrMd(DrawRP,JAM2);
                  boxon = 0;
                  xmax = ((float) (brcx-320.)/xscale) + xoff;
                  xmin = ((float) (tlcx-320.)/xscale) + xoff;
                  ymax = ((float) (brcy-100.)/(f1*xscale)) + yoff;
                  ymin = ((float) (tlcy-100.)/(f1*xscale)) + yoff;
                  x = 0;
                  y = 0;
                  xscale = 640./(xmax - xmin);
                  xoff = (xmax + xmin)/2.;
                  yoff = (ymax + ymin)/2.;
                  scaled = 1;
      	          SetAPen(DrawRP,0);
                  RectFill(DrawRP,0,0,640,200);
               }
               break;
            case MOUSEMOVE:           /* If boxon, draw box, else ignore */
               if(boxon) {
                  if(boxx >= 0) {
                     Move(DrawRP,tlcx,tlcy);   /* redraw (erase) old box */
                     Draw(DrawRP,tlcx,boxy);
                     Draw(DrawRP,boxx,boxy);
                     Draw(DrawRP,boxx,tlcy);
                     Draw(DrawRP,tlcx,tlcy);
                  }
                  boxx = message->MouseX;
                  boxy = message->MouseY;
                  Move(DrawRP,tlcx,tlcy);   /* draw new box */
                  Draw(DrawRP,tlcx,boxy);
                  Draw(DrawRP,boxx,boxy);
                  Draw(DrawRP,boxx,tlcy);
                  Draw(DrawRP,tlcx,tlcy);
               }
               break;
         }
         ReplyMsg(message);
      }
      while (message = (struct IntuiMessage *)GetMsg(g_window->UserPort)) {
/* Something happened in the gadget window. */
      	 if (message->Class == GADGETUP) {
      	    if (sscanf(input,"%f,%f,%f,%d", &a, &b, &c, &num) != 4) {
      	    	sprintf(input,"Error scanning input.  Reenter.");
      	    	Move(GadgRP,10,20);
      	    	Text(GadgRP,input,sizeof(input));
      	    } 
      	    else {
      	       if(savea != a || saveb != b || savec != c) {
/* At least one of the inputs has changed.  Reset everything. */
      	          x = 0;
      	          y = 0;
      	          SetAPen(DrawRP,0);
                  RectFill(DrawRP,0,0,640,200); /* erases screen */
                  scaled = 0;
      	       } 
      	       gadgflag = 0;
               savea = a;
               saveb = b;
               savec = c;
      	    }
      	 }
      	 ReplyMsg(message);
      }
   }

   if (g_window) CloseWindow(g_window);
   g_window = NULL;
   myInit(a, b, c, &xscale, &xoff, &yoff);
   dif = num/12;

/* This is the main loop.  Everything in here should be FFP or integer */
/* First load up FFP variables. */
   affp.f = a;
   bffp.f = b;
   cffp.f = c;
   xscffp.f = xscale;
   xoffp.f = xoff;
   yoffp.f = yoff;
   f1ffp.f = f1;
   xffp.f = x;
   yffp.f = y; 
   affp.i = SPFieee(affp.i);
   bffp.i = SPFieee(bffp.i);
   cffp.i = SPFieee(cffp.i);
   xffp.i = SPFieee(xffp.i);
   yffp.i = SPFieee(yffp.i);
   xscffp.i = SPFieee(xscffp.i);
   xoffp.i = SPFieee(xoffp.i);
   yoffp.i = SPFieee(yoffp.i);
   f1ffp.i = SPFieee(f1ffp.i);
   
/* Now do loop */
   for (i = 1; i < num; i++) {
      color = (i/dif)+4;
      SetAPen(DrawRP,color);
      x0 = 320 + (int) SPTieee(SPMul(SPSub(xoffp.i,xffp.i),xscffp.i));
      y0 = 100 + (int) SPTieee(SPMul(SPMul(f1ffp.i,xscffp.i),
      				     	SPSub(yoffp.i,yffp.i)));
      WritePixel(DrawRP, x0, y0);
      xx.i = SPSub( SPMul(Sign(xffp.i), SPSqrt(SPAbs(
      			SPSub(cffp.i,SPMul(bffp.i,xffp.i))))),yffp.i);
      yy.i = SPSub(xffp.i,affp.i);
      xffp.i = xx.i;
      yffp.i = yy.i;
      while (message = (struct IntuiMessage *)GetMsg(window->UserPort)) {
         if (message->Class == CLOSEWINDOW) {
            ReplyMsg(message);
            Cleanup(NULL);
         }
         ReplyMsg(message);
      }
   }  
/* Done with loop, now convert final x, y from FFP to float */
   x = SPTieee(xffp.i);
   y = SPTieee(yffp.i);   
   goto reset;        
}

Sign(x)
/* The sign bit of an FFP number is in bit 7.  It is 0 for positive,
   and 1 for negative. */
int x;
{
   FFP xp;
   BYTE signbit;
   
   signbit = (x & 0x80L) >> 7;
   if (signbit) {
      xp.f = -1;
      xp.i = SPFieee(xp.i);
   } else {
      xp.f = 0;
      xp.i = SPFieee(xp.i);
   }
   return(xp.i);     /* Returns as integer, but will be interpreted as FFP */
}

Sign2(x)

float x;
{
   int i;
   
   i = (x >= 0) ?  1 : -1 ;
   return(i);
}
  
myInit(a, b, c, xscale, xoff, yoff)
/********************************************************************
 *   This routine calculates the first 100 points and keeps track   *
 *   of the highest and lowest x and y values, then uses them for   *
 *   setting the scale of the screen.  The screen is scaled so that *
 *   the pattern takes up the middle quarter of the screen.  The    *
 *   x scale is returned in *xscale, and the world coordinate       *
 *   values of the center of the screen are returned as *xoff and   * 
 *   *yoff.							    *
 *******************************************************************/ 
FLOAT a, b, c;
FLOAT *xscale, *xoff, *yoff;
{
   int i;
   FLOAT ymax = -1.e10, ymin = 1.e10;
   FLOAT xmax = -1.e10, xmin = 1.e10;
   FLOAT x = 0, y = 0;
   FLOAT xx = 0, yy = 0;
 
   if(scaled) return(0);
   for (i = 0; i < 100; i++) {
      xx = y - (FLOAT) Sign2(x) * sqrt(abs(b * x - c));
      yy = a - x;
      xmax = (xx > xmax) ? xx : xmax;
      xmin = (xx < xmin) ? xx : xmin;
      ymax = (yy > ymax) ? yy : ymax;
      ymin = (yy < ymin) ? yy : ymin;
      x = xx;
      y = yy;
   }
/*  These are the world coordinates of the center of the screen.  */
   *xoff = (xmax + xmin)/2.;
   *yoff = (ymax + ymin)/2.;
   *xscale = 210./(xmax - xmin);
/*   These are for diagnostics only...
   SetAPen(DrawRP,4);
   sprintf(outtext,"xx = %f, xn = %f, yx = %f, yn = %f",xmax,xmin,ymax,ymin);
   Move(DrawRP, 10, 100);
   Text(DrawRP,outtext,sizeof(outtext));
   sprintf(outtext,"xo=%f, yo=%f, xsc=%f",*xoff,*yoff,*xscale);
   Move(DrawRP, 10, 110);
   Text(DrawRP,outtext,sizeof(outtext));
*/
   return(0);
}


Cleanup (ExitText)

char *ExitText;
{
   if (g_window) CloseWindow(g_window);
   if (window) CloseWindow(window);
   if (screen) CloseScreen(screen);
   if (GfxBase) CloseLibrary(GfxBase);
   if (MathBase) CloseLibrary(MathBase);
   if (MathTransBase) CloseLibrary(MathTransBase);
   if (ExitText) fprintf(stderr, ExitText);
   exit (!!ExitText);   /* NULL returns 0, all else returns 1 */
}

void add_gadgets(Screen)

/* Open gadget window, complete with string gadget. */

struct Screen *Screen;
{
   static char undo[40] = "";

   static struct IntuiText help_text = {
      0, 1, JAM2,
      4, -10, NULL,
      "Enter four numbers a, b, c, num",
      NULL
   };

   static struct StringInfo hslstring   = {
      input, undo,             /* input string and undo string */
      0, sizeof(input),        /* initial and max cursor position */
      0,                       /* position of first displayed char */
      0, 0, sizeof(input), 0, 0, NULL, 0, NULL
   };

   static struct Gadget input_gadget = {
      NULL, 10, 20, 320, 10, GADGHCOMP,
      RELVERIFY, STRGADGET, NULL, NULL,
      &help_text, NULL, (APTR)&hslstring, 0, NULL
   };

   static struct NewWindow gadget_window = {
      100, 50, 420, 30, -1, -1, GADGETUP | GADGETDOWN,
      WINDOWDEPTH | WINDOWDRAG | SMART_REFRESH,
      &input_gadget, NULL, "Input", 
      NULL, NULL, 0, 0, 0, 0,
      CUSTOMSCREEN
   };

   gadget_window.Screen = Screen;
   if ((g_window = (struct Window *)OpenWindow(&gadget_window)) == NULL)
      Cleanup("Could not open gadget window.");
}

