/* FastGro 1.0   - a simulation of Diffusion-Limited Aggregation

	Copyright (c) 1989  Doug Houck (03-Feb-89)

	This program has been placed in the PUBLIC DOMAIN, so anyone,
	anywhere can do anything they want with it.

	This was compiled with the Aztec C compiler, version 3.6a, using
	the large data, large code, small integer model. "cc +d +d ..."
	and linked with "ln FastGro.o Wander.o -lml -lcl"
*/

#include <exec/types.h>
#include <intuition/intuition.h>
#include <functions.h>			/* learn about system */

#define RANDOMS		50437		/* how many random #s to generate */
#define STARTING_POINTS	200		/* points around circle for starting */

#define ffp long			/* Motorola Fast Floating Point */
extern ffp
	ran(),				/* use manx random # generator */
	SPFlt(), SPFix(), SPMul(), SPDiv(),
	SPSin(), SPCos(), SPAdd(), SPSub();

struct Screen *hires_screen	= NULL;
struct Window *hires_window	= NULL;
struct RastPort *hires_rp	= NULL;
struct MsgPort *window_port	= NULL;

long MathBase			= NULL,	/* library bases */
	MathTransBase		= NULL,
	IntuitionBase		= NULL,
	GfxBase			= NULL;

int	rows, columns,			/* size of screen in pixels */
	maxrow, maxcolumn,
	columnsdiv8; 			/* in bytes */
	
long radius, color_iterations,
	points, points_to_go;		/* parameters for drawing */


char *row[550];				/* pointers to rows */
char *row_memory = NULL;		/*   in row memory */
char *border[550];			/* pointers to rows */
char *border_memory = NULL;		/*   in border memory */
long optimizer_memory;			/* how much space used */

char *randoms = NULL;			/* pointer to random array */
char *next_random;			/* index into randoms */

struct start_t				/* cache starting points */
{  int x, y;
};
struct start_t start[STARTING_POINTS];

struct NewScreen new_hires_screen =
{  0,0,
   640, STDSCREENHEIGHT,		/* size from Workbench screen */	
   4,          /* 16 colors */
   2,1,        /* pens */
   HIRES | LACE,
   CUSTOMSCREEN | SHOWTITLE | SCREENQUIET | SCREENBEHIND,
   NULL,
   NULL,
   NULL,
   NULL
};


struct NewWindow new_hires_window =
{  0,0,        /* coords */
   640,400,
   0,1,      /* pens */
   MOUSEBUTTONS,
   BACKDROP  |  BORDERLESS,
   NULL,
   NULL,
   NULL,           /* title */
   NULL,           /* screen */
   NULL,           /* bitmap */
   0,0,0,0,        /* min, max */
   CUSTOMSCREEN
};


initialize(argc, argv)		/* open libraries, allocate memory... */
   int argc;
   char **argv;
{
   IntuitionBase = (long)OpenLibrary( "intuition.library",33L);
   if (!IntuitionBase)
   {  ReportProblem("I Really Needed Kickstart/Workbench 1.2");
      return(0);
   }

   GfxBase = (long)OpenLibrary( "graphics.library",33L);
   if (!GfxBase)
   {  ReportProblem("can't open graphics.library");
      return(FALSE);
   }

   MathBase = (long)OpenLibrary( "mathffp.library",33L);
   if (!MathBase)
   {  ReportProblem("can't open mathffp.library");
      return(FALSE);
   }

   MathTransBase = (long)OpenLibrary( "mathtrans.library",33L);
   if (!MathTransBase)
   {  ReportProblem("Would kinda liketa have 'mathtrans.library' 1.2, mebbee?");
      return(FALSE);
   }

   rows = DisplayRows()<<1;		/* get # of rows in WB screen */
   maxrow = rows-1;
   columns = DisplayColumns()<<1;	/* get # of columns in WB screen */
   columnsdiv8 = (columns+7)>>3;
   maxcolumn = columns-1;  
   optimizer_memory = rows * columnsdiv8; /* space arrays will use */

/* ------------------------------- display screen ---------------------- */
   new_hires_screen.Height = rows;
   new_hires_screen.Width = columns;

   hires_screen = OpenScreen( &new_hires_screen );
   if (!hires_screen)
   {  ReportProblem("No Memory for HiRes Screen.  Sorry!");
      return(0);
   }

   new_hires_window.Screen = hires_screen;
   new_hires_window.Width = hires_screen->Width;
   new_hires_window.Height = hires_screen->Height;
   hires_window = OpenWindow( &new_hires_window );
   if (!hires_window )
   {  ReportProblem("Can't open hires window");
      return(0);
   }
   hires_rp = hires_window->RPort;
   window_port = hires_window->UserPort;

   {  long red, green, blue;		/* set up colors */
      green = 12;
      for( red = 0, blue = 15;
           red <= 15;
           red++, blue--
         )
         SetRGB4( &hires_screen->ViewPort, red, red, green, blue );
      SetRGB4( &hires_screen->ViewPort, 0L, 0L, 0L, 0L );
   }

/* ---------------------- allocate optimizing arrays --------------------- */

   row_memory = (char*)AllocMem( optimizer_memory, 0L );
   border_memory = (char*)AllocMem( optimizer_memory, 0L );
   if (!row_memory || !border_memory)
   {  ReportProblem("No Memory For Auxiliary Arrays");
      return(FALSE);
   }

   {  int i = 0;			/* init row pointers into arrays */
      char *row_ptr = row_memory;
      char *border_ptr = border_memory;

      for( i=0; i < rows; i++ )
      {  row[i]    = row_ptr;
         border[i] = border_ptr;
         row_ptr    += columnsdiv8;
         border_ptr += columnsdiv8;
      }
   }

   randoms = (char*)AllocMem( (long)RANDOMS, 0L );
   if (!randoms)
   {  ReportProblem( "no random array");
      return(0);
   }
   {  long counter = RANDOMS-1;		/* init random array */
      register char *ptr = randoms;
      long scaler;
      scaler = SPFlt( 254L );
      while(--counter)
        *ptr++ = SPFix( SPMul(ran(), scaler ) )+ 1;
      *ptr = 0;				/* 0 marks the end */
      next_random = randoms;
   }
 
   radius = 70;
   color_iterations = 100;
   points = 5000;

   return( TRUE );
}

wrapup()	/* clean up, close libraries, deallocate, wipe nose, etc */
{
   if (randoms)	      FreeMem       ( randoms,       (long)RANDOMS    );
   if (row_memory)    FreeMem       ( row_memory,    optimizer_memory );	
   if (border_memory) FreeMem       ( border_memory, optimizer_memory );
   if (hires_window)  CloseWindow   ( hires_window  );
   if (hires_screen)  CloseScreen   ( hires_screen  );
   if (IntuitionBase) CloseLibrary  ( IntuitionBase );
   if (GfxBase)       CloseLibrary  ( GfxBase       );
   if (MathTransBase) CloseLibrary  ( MathTransBase );
   if (MathBase)      CloseLibrary  ( MathBase      );

  exit( 0 );
}

ReportProblem( text )
   char *text;
{  char answer[64];
   printf("Had a slight problem...\n\n");
   printf("    %s\n\n", text);
   printf("Press <RETURN> to abort.  ");
   gets( answer );
}

mark_perimeter( x, y )	/* mark perimeter around a pixel */
   int x, y;
{  setbit( row, x, y );

   if (y>0)          setbit( row, x  , y-1 );	/* careful of edges... */
   if (y<maxrow)     setbit( row, x  , y+1 );
 
   if (x>0)
   {  if (y>0)       setbit( row, x-1, y-1 );
                     setbit( row, x-1, y   );
      if (y<maxrow)  setbit( row, x-1, y+1 );
   }
   if (x<maxcolumn)
   {  if (y>0)       setbit( row, x+1, y-1 );
                     setbit( row, x+1, y   );
      if (y<maxrow)  setbit( row, x+1, y+1 );
   }
}

set_up_optimizing( radius )
   long radius;
{  int x, y;
   long midx, midy;
   int steps, s, thick;
   ffp theta, stepsize, radi;

   midx = columns / 2;
   midy = rows / 2;

   for(y = 0; y<rows; y++)		/* clear everything first */
      for(x=0; x<columnsdiv8; x++)
      {  row[y][x] = 0;
         border[y][x] = 0;
      }

   steps = radius * 12;		/* mark border of circle */
   stepsize = SPDiv( SPFlt((long)steps),	/* 2 pi / steps */
	SPDiv( SPFlt(1000L), SPFlt( 6283L) ) );
   radi = SPFlt( radius );

   for(thick = 0; thick<4; thick++ )		/* make sure no leaks */
   {  for( theta = 0, s = steps;
           s;
           s--, theta = SPAdd(theta, stepsize ) )
      {  x = midx + SPFix(SPMul( SPCos(theta), radi ));
         y = midy + SPFix(SPMul( SPSin(theta), radi ));
         setbit( row, x, y );
         setbit( border, x, y );
      }
      radi = SPSub( SPDiv( SPFlt(3L),SPFlt(1L)), radi );
   }
				/* set up starting points */
 
   steps = STARTING_POINTS;
   stepsize = SPDiv( SPFlt((long)steps),SPDiv( SPFlt(1000L), SPFlt( 6283L) ) );
   radi = SPFlt( radius - 3L );

   for( theta = 0, steps = 0;
        steps < STARTING_POINTS;
        steps++, theta = SPAdd(theta, stepsize )
      )
   {  start[steps].x = midx + SPFix(SPMul( SPCos(theta), radi ));
      start[steps].y = midy + SPFix(SPMul( SPSin(theta), radi ));
   }
}

draw_fractal()
{  long max_radius, color_counter, color;
   int x,y;
   ffp start_scale;
   int start_location;
   struct IntuiMessage *im;
   char answer[64];

   max_radius = rows/2 - 2;
   start_scale = SPFlt( (long)STARTING_POINTS );

   printf("\n\nRadius (10..%ld) [%ld] ",max_radius, radius); /* get parms */
   gets( answer );
   if (answer[0])
   {  sscanf(answer, "%ld", &radius );
      if (radius < 10) radius = 10;
      if (radius > max_radius) radius = max_radius;
   }

   printf("Particles (1..1000000) [%ld] ",points);
   gets( answer );
   if (answer[0])
      sscanf(answer, "%ld", &points );
   points_to_go = points;   

   printf("Change Color After (1..1000000) [%ld] ",color_iterations);
   gets( answer );
   if (answer[0])
      sscanf( answer, "%ld", &color_iterations );
   color = 1;
   color_counter = color_iterations;

   printf("To abort drawing, click anywhere on drawing.\n");
   printf("Setting up...\n");

   set_up_optimizing( radius);

   x = columns / 2;			/* seed the center */
   y = rows / 2;
   mark_perimeter(x,y);
   WritePixel( hires_rp, (long)x, (long)y );

   SetAPen( hires_rp, 1L );		/* erase window */
   SetDrMd( hires_rp, JAM2 );
   SetRast( hires_rp, 0L );

   ScreenToFront( hires_screen );
 
					/* clean out port */
   while( im = (struct IntuiMessage*)GetMsg( window_port) )
      ReplyMsg( im );

   while(!IsMessageWaiting(window_port) && (points_to_go> 0 ))
   {  start_location = SPFix(SPMul( ran(), start_scale ));
      x = start[start_location].x;
      y = start[start_location].y;

      wander_asm( &x, &y );		/* do the time-consuming stuff */

      if (!checkbit( border, x, y ) )	/* not a border hit? OK! */
      {  mark_perimeter(x,y);
         WritePixel( hires_rp, (long)x, (long)y );
         points_to_go--;
         if( !(--color_counter) )
         {  color_counter = color_iterations;
            color++;
            if (color > 15) color = 1;
            SetAPen( hires_rp, color );
         }
      }
   }

   ScreenToBack( hires_screen);
}

main( argc, argv )
   int argc;
   char **argv;
{  char answer[32];

   printf("Welcome to FastGro, a fractal program by Doug Houck,\n");
   printf("simulating Diffusion-Limited Aggregation.  The purpose\n");
   printf("of this program is to demonstrate how selected\n");
   printf("optimizations can improve the speed of a program by\n");
   printf("an order of magnitude.  Meanwhile, I'm setting up...\n");

   if (!initialize( argc, argv ))
      wrapup();

   do
   {  draw_fractal();
      printf("More? (Y/N) [Yes] ");
      gets( answer );
   }
   while ( ! ((answer[0]=='N') || (answer[0]=='n')) );

   wrapup();
}

