/*
 * JIIM.C
 *
 * Generates Inverse Julia in real time, lets move a cursor which determines
 * the J-set.
 *
 *  The J-set is generated in a fixed-size window, a third of the screen.
 *
 * This module is linked as an overlay, use ENTER_OVLY and EXIT_OVLY.
 *
 *
 * The routines used to set/move the cursor and to save/restore the
 * window were "borrowed" from editpal.c (TW - now we *use* the editpal code)
 *     (if you don't know how to write good code, look for someone who does)
 *
 *    JJB  [jbuhler@gidef.edu.ar]
 *    TIW  Tim Wegner
 *    MS   Michael Snyder
 *    KS   Ken Shirriff
 * Revision History:
 *
 *        7-28-92       JJB  Initial release out of editpal.c
 *        7-29-92       JJB  Added SaveRect() & RestoreRect() - now the
 *                           screen is restored after leaving.
 *        7-30-92       JJB  Now, if the cursor goes into the window, the
 *                           window is moved to the other side of the screen.
 *                           Worked from the first time!
 *        10-09-92      TIW  A major rewrite that merged cut routines duplicated
 *                           in EDITPAL.C and added orbits feature.
 *        11-02-92      KS   Made cursor blink
 *        11-18-92      MS   Altered Inverse Julia to use MIIM method.
 *	  11-25-92	MS   Modified MIIM support routines to better be
 *			     shared with stand-alone inverse julia in
 *			     LORENZ.C, and to use DISKVID for swap space.
 *        05-05-93      TIW  Boy this change file really got out of date.
 *			     Added orbits capability, various commands, some 
 * 			     of Dan Farmer's ideas like circles and lines 
 *			     connecting orbits points.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef XFRACT
#include <stdarg.h>
#include <dos.h>     /* for FP_SEG & FP_OFF */
#else
#include <varargs.h>
#endif

#include <math.h>

#ifdef __TURBOC__
#   include <mem.h>   /* to get mem...() declarations */
#endif

#include "helpdefs.h"
#include "fractint.h" /* for overlay stuff */
#include "fractype.h"
#include "prototyp.h"

#define FAR_RESERVE     8192L     /* amount of far mem we will leave avail. */
#define MAXRECT         1024      /* largest width of SaveRect/RestoreRect */

#define newx(size)     mem_alloc(size)
#define delete(block)

enum stored_at_values
   {
   NOWHERE,
   DISK,
   MEMORY
   } ;

/* borrowed from LORENZ.C */
struct affine
{
   /* weird order so a,b,e and c,d,f are vectors */
   double a;
   double b;
   double e;
   double c;
   double d;
   double f;
};

extern int  setup_convert_to_screen(struct affine *);

int show_numbers =0;              /* toggle for display of coords */
int stored_at;
char far *memory = NULL;            /* pointer to saved rectangle */
FILE *file;
int windows = 0;               /* windows management system */

extern char scrnfile[];   /* file where screen portion is */
				  /* stored */
extern BYTE      *line_buff;   /* must be alloced!!! */
extern int           sxdots;             /* width of physical screen         */
extern int           sydots;             /* depth of physical screen         */
extern int           sxoffs;             /* start of logical screen          */
extern int           syoffs;             /* start of logical screen          */
extern int           strlocn[];          /* 10K buffer to store classes in   */
extern int           colors;             /* # colors avail.                  */
extern int       using_jiim;
extern int       viewwindow;     /* 0 for full screen, 1 for window */
extern float     viewreduction;  /* window auto-sizing */
extern int       viewcrop;               /* nonzero to crop default coords */
extern float     finalaspectratio;              /* for view shape and rotation */
extern int       viewxdots,viewydots;   /* explicit view sizing */
extern double    dxsize, dysize;
extern int       xdots;                  /* width of logical screen          */
extern int       ydots;                  /* depth of logical screen          */
extern double    xxmax,xxmin;    /* limits of the "window"       */
extern double    yymax,yymin;    /* on the z-plane               */
extern int           color_bright;       /* brightest color in palette       */
extern int           color_dark;         /* darkest color in palette         */
extern int           color_medium;       /* nearest to medbright gray color  */
extern int           lookatmouse;        /* mouse mode for getakey(), etc    */
extern int           maxit;                  /* try this many iterations */
extern int           fractype;       /* fractal type */
extern _CMPLX     old,new,init;
extern double tempsqrx,tempsqry;
static int xc, yc;                       /* corners of the window */
static int xd, yd;                       /* dots in the window    */
extern void displayc(int x, int y, int fg, int bg, int ch);
extern float screenaspect;
double xcjul = BIG;
double ycjul = BIG;
extern int hasinverse;

void displays(int x, int y, int fg, int bg, char *str, int len)
{
   int i;
   for(i=0;i<len; i++)
      displayc(x+i*8, y, fg, bg, str[i]);
}

/* circle routines from Dr. Dobbs June 1990 */
int xbase, ybase;
unsigned xAspect, yAspect;

void SetAspect(double aspect)
{
   xAspect = 0;
   yAspect = 0;
   aspect = fabs(aspect);
   if (aspect != 1.0)
      if (aspect > 1.0)
	 yAspect = 65536.0 / aspect;
      else
	 xAspect = 65536.0 * aspect;
}

void _fastcall c_putcolor(int x, int y, int color)
   {
   /* avoid writing outside window */
   if ( x < xc || y < yc || x >= xc + xd || y >= yc + yd )
      return ;
   if(y >= sydots - show_numbers) /* avoid overwriting coords */
      return;
   if(windows == 2) /* avoid overwriting fractal */
      if (0 <= x && x < xdots && 0 <= y && y < ydots)
	 return;
   putcolor(x, y, color);
   }


int  c_getcolor(int x, int y)
   {
   /* avoid reading outside window */
   if ( x < xc || y < yc || x >= xc + xd || y >= yc + yd )
      return 1000;
   if(y >= sydots - show_numbers) /* avoid overreading coords */
      return 1000;
   if(windows == 2) /* avoid overreading fractal */
      if (0 <= x && x < xdots && 0 <= y && y < ydots)
	 return 1000;
   return getcolor(x, y);
   }

void circleplot(int x, int y, int color)
{
   if (xAspect == 0)
      if (yAspect == 0)
	 c_putcolor(x+xbase, y+ybase,color);
      else
	 c_putcolor(x+xbase, (short)(ybase + (((long) y * (long) yAspect) >> 16)),color);
   else
      c_putcolor((int)(xbase + (((long) x * (long) xAspect) >> 16)), y+ybase, color);
}

void plot8(int x, int y, int color)
{
   circleplot(x,y,color);
   circleplot(-x,y,color);
   circleplot(x,-y,color);
   circleplot(-x,-y,color);
   circleplot(y,x,color);
   circleplot(-y,x,color);
   circleplot(y,-x,color);
   circleplot(-y,-x,color);
}

void circle(int radius, int color)
{
   int x,y,sum;

   x = 0;
   y = radius << 1;
   sum = 0;

   while (x <= y)
   {
      if ( !(x & 1) )   /* plot if x is even */
	 plot8( x >> 1, (y+1) >> 1, color);
      sum += (x << 1) + 1;
      x++;
      if (sum > 0)
      {
	 sum -= (y << 1) - 1;
	 y--;
      }
   }
}


/*
 * MIIM section:
 *
 * Global variables and service functions used for computing
 * MIIM Julias will be grouped here (and shared by code in LORENZ.C)
 *
 */


 long   ListFront, ListBack, ListSize;  /* head, tail, size of MIIM Queue */
 long   lsize, lmax;			/* how many in queue (now, ever) */
 int    maxhits = 1;
 int    OKtoMIIM;
 int    SecretExperimentalMode;
 float  luckyx = 0, luckyy = 0;
 extern int debugflag;
 extern int           bitshift;         /* bit shift for fudge */
 extern long          fudge;            /* fudge factor (2**n) */


long lsqrt(long f)
{
    int N;
    unsigned long y0, z;
    static long a=0, b=0, c=0;                  /* constant factors */

    if (f == 0)
	return f;
    if (f <  0)
	return 0;

    if (a==0)                                   /* one-time compute consts */
    {
	a = fudge * .41731;
	b = fudge * .59016;
	c = fudge * .7071067811;
    }

    N  = 0;
    while (f & 0xff000000L)                     /* shift arg f into the */
    {                                           /* range: 0.5 <= f < 1  */
	N++;
	f /= 2;
    }
    while (!(f & 0xff800000L))
    {
	N--;
	f *= 2;
    }

    y0 = a + multiply(b, f,  bitshift);         /* Newton's approximation */

    z  = y0 + divide (f, y0, bitshift);
    y0 = (z>>2) + divide(f, z,  bitshift);

    if (N % 2)
    {
	N++;
	y0 = multiply(c,y0, bitshift);
    }
    N /= 2;
    if (N >= 0)
	return y0 <<  N;                        /* correct for shift above */
    else
	return y0 >> -N;
}

#define SinCosFudge 0x10000L


LCMPLX
ComplexSqrtLong(long x, long y)
{
   double    mag, theta;
   long      maglong, thetalong;
   LCMPLX    result;

#if 1
   mag       = sqrt(sqrt(((double) multiply(x,x,bitshift))/fudge +
			 ((double) multiply(y,y,bitshift))/ fudge));
   maglong   = mag * fudge;
#else
   maglong   = lsqrt(lsqrt(multiply(x,x,bitshift)+multiply(y,y,bitshift)));
#endif
   theta     = atan2((double) y/fudge, (double) x/fudge)/2;
   thetalong = theta * SinCosFudge;
   SinCos086(thetalong, &result.y, &result.x);
   result.x  = multiply(result.x << (bitshift - 16), maglong, bitshift);
   result.y  = multiply(result.y << (bitshift - 16), maglong, bitshift);
   return result;
}

_CMPLX
ComplexSqrtFloat(double x, double y)
{
   double mag   = sqrt(sqrt(x*x + y*y));
   double theta = atan2(y, x) / 2;
   _CMPLX  result;

   FPUsincos(&theta, &result.y, &result.x);
   result.x *= mag;
   result.y *= mag;
   return result;
}


extern char dstack[];

static void fillrect(int x, int y, int width, int depth, int color)
{
   /* fast version of fillrect */
   if(hasinverse == 0)
      return;
   memset(dstack, color % colors, width);
   while (depth-- > 0)
   {
      if(keypressed()) /* we could do this less often when in fast modes */
         return;
      putrow(x, y++, width, dstack);
   }
}

/*
 * Queue/Stack Section:
 *
 * Defines a buffer that can be used as a FIFO queue or LIFO stack.
 */

int
QueueEmpty()		/* True if NO points remain in queue */
{
   return (ListFront == ListBack);
}

int
QueueFull()		/* True if room for NO more points in queue */
{
   return (((ListFront + 1) % ListSize) == ListBack);
}

int
QueueFullAlmost()	/* True if room for ONE more point in queue */
{
   return (((ListFront + 2) % ListSize) == ListBack);
}

void
ClearQueue()
{
   ListFront = ListBack = lsize = lmax = 0;
}


/*
 * Queue functions for MIIM julia:
 * move to JIIM.C when done
 */

extern long  ListSize, ListFront, ListBack, lsize, lmax;
extern float luckyx, luckyy;

int Init_Queue(unsigned long request)
{
   extern int _fastcall common_startdisk(long, long, int);
   extern unsigned int xmmlongest();
   extern unsigned int xmmreallocate(unsigned int, unsigned int);
   extern int dotmode;
   unsigned int        largest;


   if (dotmode == 11)
   {
      static char far *nono = "Don't try this in disk video mode, kids...\n";
      stopmsg(0, nono);
      ListSize = 0;
      return 0;
   }

#if 0
   if (xmmquery() && debugflag != 420)	/* use LARGEST extended mem */
      if ((largest = xmmlongest()) > request / 128)
	 request   = (unsigned long) largest * 128L;
#endif

   for (ListSize = request; ListSize > 1024; ListSize -= 512)
      switch (common_startdisk(ListSize * 8, 1, 256))
      {
	 case 0:                        /* success */
	    ListFront = ListBack = 0;
	    lsize = lmax = 0;
	    return 1;
	 case -1:
	    continue;			/* try smaller queue size */
	 case -2:
	    ListSize = 0;               /* cancelled by user      */
	    return 0;
      }

   /* failed to get memory for MIIM Queue */
   ListSize = 0;
   return 0;
}

void
Free_Queue()
{
   enddisk();
   ListFront = ListBack = ListSize = lsize = lmax = 0;
}

extern int ToMemDisk  (long, int, void far *);
extern int FromMemDisk(long, int, void far *);

int
PushLong(long x, long y)
{
   if (((ListFront + 1) % ListSize) != ListBack)
   {
      if (ToMemDisk(8*ListFront, sizeof(x), &x) &&
	  ToMemDisk(8*ListFront +sizeof(x), sizeof(y), &y))
      {
	 ListFront = (ListFront + 1) % ListSize;
	 if (++lsize > lmax)
	 {
	    lmax   = lsize;
	    luckyx = x;
	    luckyy = y;
	 }
	 return 1;
      }
   }
   return 0;			/* fail */
}

int
PushFloat(float x, float y)
{
   if (((ListFront + 1) % ListSize) != ListBack)
   {
      if (ToMemDisk(8*ListFront, sizeof(x), &x) &&
	  ToMemDisk(8*ListFront +sizeof(x), sizeof(y), &y))
      {
	 ListFront = (ListFront + 1) % ListSize;
	 if (++lsize > lmax)
	 {
	    lmax   = lsize;
	    luckyx = x;
	    luckyy = y;
	 }
	 return 1;
      }
   }
   return 0;			/* fail */
}

_CMPLX
PopFloat()
{
   _CMPLX pop;
   float  popx, popy;

   if (!QueueEmpty())
   {
      ListFront--;
      if (ListFront < 0)
	  ListFront = ListSize - 1;
      if (FromMemDisk(8*ListFront, sizeof(popx), &popx) &&
	  FromMemDisk(8*ListFront +sizeof(popx), sizeof(popy), &popy))
      {
	 pop.x = popx;
	 pop.y = popy;
	 --lsize;
      }
      return pop;
   }
   pop.x = 0;
   pop.y = 0;
   return pop;
}

LCMPLX
PopLong()
{
   LCMPLX pop;

   if (!QueueEmpty())
   {
      ListFront--;
      if (ListFront < 0)
	  ListFront = ListSize - 1;
      if (FromMemDisk(8*ListFront, sizeof(pop.x), &pop.x) &&
	  FromMemDisk(8*ListFront +sizeof(pop.x), sizeof(pop.y), &pop.y))
	 --lsize;
      return pop;
   }
   pop.x = 0;
   pop.y = 0;
   return pop;
}

int
EnQueueFloat(float x, float y)
{
   return PushFloat(x, y);
}

int
EnQueueLong(long x, long y)
{
   return PushLong(x, y);
}

_CMPLX
DeQueueFloat()
{
   _CMPLX out;
   float outx, outy;

   if (ListBack != ListFront)
   {
      if (FromMemDisk(8*ListBack, sizeof(outx), &outx) &&
	  FromMemDisk(8*ListBack +sizeof(outx), sizeof(outy), &outy))
      {
	 ListBack = (ListBack + 1) % ListSize;
	 out.x = outx;
	 out.y = outy;
	 lsize--;
      }
      return out;
   }
   out.x = 0;
   out.y = 0;
   return out;
}

LCMPLX
DeQueueLong()
{
   LCMPLX out;
   out.x = 0;
   out.y = 0;

   if (ListBack != ListFront)
   {
      if (FromMemDisk(8*ListBack, sizeof(out.x), &out.x) &&
	  FromMemDisk(8*ListBack +sizeof(out.x), sizeof(out.y), &out.y))
      {
	 ListBack = (ListBack + 1) % ListSize;
	 lsize--;
      }
      return out;
   }
   out.x = 0;
   out.y = 0;
   return out;
}



/*
 * End MIIM section;
 */



static BOOLEAN MemoryAlloc(long size)
{
   char far *temp;
   
   if (debugflag == 420)
      return(FALSE);
   temp = (char far *)farmemalloc(FAR_RESERVE);   /* minimum free space */

   if (temp == NULL)
   {
      stored_at = NOWHERE;
      return (FALSE);   /* can't do it */
   }

   memory = (char far *)farmemalloc( size );
   farmemfree(temp);

   if ( memory == NULL )
   {
      stored_at = NOWHERE;
      return (FALSE);
   }
   else
   {
      stored_at = MEMORY;
      return (TRUE);
   }
}


static void SaveRect(int x, int y, int width, int depth)
{
   char buff[MAXRECT];
   int  yoff;
   if(hasinverse == 0)
      return;
   /* first, do any de-allocationg */

   switch( stored_at )
   {
   case NOWHERE:
      break;

   case DISK:
      break;

   case MEMORY:
      if (memory != NULL)
      {
	 farmemfree(memory);
      }
      memory = NULL;
      break;
   }

   /* allocate space and store the rect */

   memset(dstack, color_dark, width);
   if ( MemoryAlloc( (long)width*(long)depth) )
   {
      char far  *ptr = memory;
      char far  *bufptr = buff; /* MSC needs this indirection to get it right */

      Cursor_Hide();
      for (yoff=0; yoff<depth; yoff++)
      {
	 getrow(x, y+yoff, width, buff);
	 putrow(x, y+yoff, width, dstack);
	 movedata(FP_SEG(bufptr), FP_OFF(bufptr), FP_SEG(ptr), FP_OFF(ptr), width);
	 ptr = (char far *)normalize(ptr+width);
      }
      Cursor_Show();
   }

   else /* to disk */
   {
      stored_at = DISK;

      if ( file == NULL )
      {
	 file = fopen(scrnfile, "w+b");
	 if (file == NULL)
	 {
	    stored_at = NOWHERE;
	    buzzer(3);
	    return ;
	 }
      }

      rewind(file);
      Cursor_Hide();
      for (yoff=0; yoff<depth; yoff++)
      {
	 getrow(x, y+yoff, width, buff);
	 putrow(x, y+yoff, width, dstack);
	 if ( fwrite(buff, width, 1, file) != 1 )
	 {
	    buzzer(3);
	    break;
	 }
      }
      Cursor_Show();
   }
}


static void RestoreRect(x, y, width, depth)
{
   char buff[MAXRECT];
   int  yoff;
   if(hasinverse == 0)
      return;

   switch ( stored_at )
   {
   case DISK:
      rewind(file);
      Cursor_Hide();
      for (yoff=0; yoff<depth; yoff++)
      {
	 if ( fread(buff, width, 1, file) != 1 )
	 {
	    buzzer(3);
	    break;
	 }
	 putrow(x, y+yoff, width, buff);
      }
      Cursor_Show();
      break;

   case MEMORY:
      {
	 char far  *ptr = memory;
	 char far  *bufptr = buff; /* MSC needs this indirection to get it right */

	 Cursor_Hide();
	 for (yoff=0; yoff<depth; yoff++)
	 {
	    movedata(FP_SEG(ptr), FP_OFF(ptr), FP_SEG(bufptr), FP_OFF(bufptr), width);
	    putrow(x, y+yoff, width, buff);
	    ptr = (char far *)normalize(ptr+width);
	 }
	 Cursor_Show();
	 break;
      }

   case NOWHERE:
      break;
   } /* switch */
}

/*
 * interface to FRACTINT
 */
int jfractype;

extern int row, col;
extern double far *dx0, far *dy0;
extern double far *dx1, far *dy1;
extern int integerfractal;
extern long far *lx0, far *ly0;
extern long far *lx1, far *ly1;
extern int bitshift;
extern int helpmode;

/* the following macros and function call the setup, per_pixel, and orbit
   routines and calculate an orbit at row 0 column 0. Have to save and 
   restore the first elements of dx0 ... dy1 as well as row and col */

#define PER_IMAGE fractalspecific[o_fractype].per_image
#define PER_PIXEL fractalspecific[o_fractype].per_pixel
#define ORBITCALC   fractalspecific[o_fractype].orbitcalc

int do_fractal_routines(double cr, double ci, int (*func)())
{
   int ret;
   int old_row, old_col;
   double old_dx0, old_dx1, old_dy0, old_dy1;
   old_dx0 = *dx0; old_dx1 = *dx1;
   old_dy0 = *dy0; old_dy1 = *dy1;
   old_row = row;  old_col = col;
   row = col = 0;
   *dx0 = cr; *dy0 = ci; *dx1 = *dy1 = 0.0;
   ret = func();
   *dx0 = old_dx0; *dx1 = old_dx1;
   *dy0 = old_dy0; *dy1 = old_dy1;
   row = old_row;  col = old_col;
   return(ret);
}


void Jiim(int which)         /* called by fractint */
{
   struct affine cvt;
   int exact = 0;
   int oldhelpmode;
   int count = 0;            /* coloring julia */
   static int mode = 0;      /* point, circle, ... */
   int       oldlookatmouse = lookatmouse;
   double cr, ci, r;
   int xfactor, yfactor;             /* aspect ratio          */

   int xoff, yoff;                   /* center of the window  */
   int x, y;
   int still, kbdchar;
   int xcrsr,ycrsr;     /* coords of the cursor / offsets to move it  */
   int iter;
   int color;
   float zoom;
   int oldsxoffs, oldsyoffs;
   int savehasinverse;
   int (*oldcalctype)();
   extern int (*calctype)();
   int o_fractype;
   int old_x, old_y;
   double aspect;
   static int randir = 0;
   static int rancnt = 0;
   static _CMPLX SaveC = {-3000.0, -3000.0};
   int actively_computing = 1;
   int first_time = 1;

   ENTER_OVLY(OVLY_ROTATE);
   /* must use standard fractal and have a float variant */
   if(fractalspecific[fractype].calctype != StandardFractal ||
	 (fractalspecific[fractype].isinteger &&
	 fractalspecific[fractype].tofloat == NOFRACTAL))
   {
       EXIT_OVLY;
       return;
   }
   oldhelpmode = helpmode;
   if(which == JIIM)
      helpmode = HELP_JIIM;
   else
   {
      helpmode = HELP_ORBITS;
      hasinverse = 1;
   }
   if(fractalspecific[fractype].isinteger)
      o_fractype = fractalspecific[fractype].tofloat;
   else
      o_fractype = fractype;
   oldsxoffs = sxoffs;
   oldsyoffs = syoffs;
   oldcalctype = calctype;
   show_numbers = 0;
   using_jiim = 1;
   mem_init(strlocn, 10*1024);
   line_buff = newx(max(sxdots,sydots));
   aspect = ((double)xdots*3)/((double)ydots*4);  /* assumes 4:3 */
	 actively_computing = 1;
   SetAspect(aspect);
   lookatmouse = 3;
   Cursor_Construct();

/*
 * MIIM code:
 * Grab far memory for Queue/Stack before SaveRect gets it.
 */
   OKtoMIIM  = 0;
   if (which == JIIM && debugflag != 300)
      OKtoMIIM = Init_Queue((long)8*1024); /* Queue Set-up Successful? */

   maxhits = 1;
   if (which == ORBIT)
      plot = c_putcolor;     		/* for line with clipping */

/*
 * end MIIM code.
 */


   if(sxoffs != 0 || syoffs != 0) /* we're in view windows */
   {
      savehasinverse = hasinverse;
      hasinverse = 1;
      SaveRect(0,0,xdots,ydots);
      sxoffs = 0;
      syoffs = 0;
      RestoreRect(0,0,xdots,ydots);
      hasinverse = savehasinverse;
   }

   if(which == ORBIT)
      do_fractal_routines(cr, ci,PER_IMAGE);
   else
      color = color_bright;

   oldhelpmode = helpmode;
   if(which == JIIM)
      helpmode = HELP_JIIM;
   else
      helpmode = HELP_ORBITS;

   if(xdots == sxdots || ydots == sydots ||
       sxdots-xdots < sxdots/3 ||
       sydots-ydots < sydots/3 ||
       xdots >= MAXRECT )
   {
      /* this mode puts orbit/julia in an overlapping window 1/3 the size of
	 the physical screen */
      windows = 0; /* full screen or large view window */
      xd = sxdots / 3;
      yd = sydots / 3;
      xc = xd * 2;
      yc = yd * 2;
      xoff = xd * 5 / 2;
      yoff = yd * 5 / 2;
   }
   else if(xdots > sxdots/3 && ydots > sydots/3)
   {
      /* Julia/orbit and fractal don't overlap */
      windows = 1;
      xd = sxdots-xdots;
      yd = sydots-ydots;
      xc = xdots;
      yc = ydots;
      xoff = xc + xd/2;
      yoff = yc + yd/2;

   }
   else
   {
      /* Julia/orbit takes whole screen */
      windows = 2;
      xd = sxdots;
      yd = sydots;
      xc = 0;
      yc = 0;
      xoff = xd/2;
      yoff = yd/2;
   }

   xfactor = xd/5.33;
   yfactor = -yd/4;

   if(windows == 0)
      SaveRect(xc,yc,xd,yd);
   else if(windows == 2)  /* leave the fractal */
   {
      fillrect(xdots, yc, xd-xdots, yd, color_dark);
      fillrect(xc   , ydots, xdots, yd-ydots, color_dark);
   }
   else  /* blank whole window */
      fillrect(xc, yc, xd, yd, color_dark);

   setup_convert_to_screen(&cvt);

   /* reuse last location if inside window */
   xcrsr = cvt.a*SaveC.x + cvt.b*SaveC.y + cvt.e + .5;
   ycrsr = cvt.c*SaveC.x + cvt.d*SaveC.y + cvt.f + .5;
   if(xcrsr < 0 || xcrsr >= xdots ||
      ycrsr < 0 || ycrsr >= ydots)
   {
      cr = (xxmax + xxmin) / 2.0;
      ci = (yymax + yymin) / 2.0;
   }
   else
   {
      cr = SaveC.x;
      ci = SaveC.y;
   }

   old_x = old_y = -1;

   xcrsr = cvt.a*cr + cvt.b*ci + cvt.e + .5;
   ycrsr = cvt.c*cr + cvt.d*ci + cvt.f + .5;

   Cursor_SetPos(xcrsr, ycrsr);
   Cursor_Show();
   color = color_bright;

   iter = 1;
   still = 1;
   zoom = 1;

#ifdef XFRACT
   Cursor_StartMouseTracking();
#endif

   while (still)
   {
      int dxcrsr, dycrsr;
      if (actively_computing) {
	  Cursor_CheckBlink();
      } else {
	  Cursor_WaitKey();
      }
      if(keypressed() || first_time) /* prevent burning up UNIX CPU */
      {
	 first_time = 0;
	 while(keypressed())
	 {
	    Cursor_WaitKey();
	    kbdchar = getakey();

	    dxcrsr = dycrsr = 0;
	    xcjul = BIG;
	    ycjul = BIG;
	    switch (kbdchar)
	    {
            case 1143:    /* ctrl - keypad 5 */
	    case 1076:    /* keypad 5        */
	       break;     /* do nothing */
	    case CTL_PAGE_UP:
	       dxcrsr = 4;
	       dycrsr = -4;
	       break;
	    case CTL_PAGE_DOWN:
	       dxcrsr = 4;
	       dycrsr = 4;
	       break;
	    case CTL_HOME:
	       dxcrsr = -4;
	       dycrsr = -4;
	       break;
	    case CTL_END:
	       dxcrsr = -4;
	       dycrsr = 4;
	       break;
	    case PAGE_UP:
	       dxcrsr = 1;
	       dycrsr = -1;
	       break;
	    case PAGE_DOWN:
	       dxcrsr = 1;
	       dycrsr = 1;
	       break;
	    case HOME:
	       dxcrsr = -1;
	       dycrsr = -1;
	       break;
	    case END:
	       dxcrsr = -1;
	       dycrsr = 1;
	       break;
	    case UP_ARROW:
	       dycrsr = -1;
	       break;
	    case DOWN_ARROW:
	       dycrsr = 1;
	       break;
	    case LEFT_ARROW:
	       dxcrsr = -1;
	       break;
	    case RIGHT_ARROW:
	       dxcrsr = 1;
	       break;
	    case UP_ARROW_2:
	       dycrsr = -4;
	       break;
	    case DOWN_ARROW_2:
	       dycrsr = 4;
	       break;
	    case LEFT_ARROW_2:
	       dxcrsr = -4;
	       break;
	    case RIGHT_ARROW_2:
	       dxcrsr = 4;
	       break;
	    case 'z':
	    case 'Z':
	       zoom = 1.0;
	       break;
	    case '<':
	    case ',':
	       zoom /= 1.15;
	       break;
	    case '>':
	    case '.':
	       zoom *= 1.15;
	       break;
	    case SPACE:
	       xcjul = cr;
	       ycjul = ci;
	       goto finish;
	       break;
	    case 'c':   /* circle toggle */
	    case 'C':   /* circle toggle */
	       mode = mode ^ 1;
	       break;
	    case 'l':
	    case 'L':
	       mode = mode ^ 2;
	       break;
	    case 'n':
	    case 'N':
	       show_numbers = 8 - show_numbers;
	       if(windows == 0 && show_numbers == 0)
	       {
		  Cursor_Hide();
		  cleartempmsg();
		  Cursor_Show();
	       }
	       break;
	    case 'p':
	    case 'P':
	       get_a_number(&cr,&ci);
               exact = 1;
               xcrsr = cvt.a*cr + cvt.b*ci + cvt.e + .5;
               ycrsr = cvt.c*cr + cvt.d*ci + cvt.f + .5;
               dxcrsr = dycrsr = 0;
	       break;
	    case 'h':   /* hide fractal toggle */
	    case 'H':   /* hide fractal toggle */
	       if(windows == 2)
		  windows = 3;
	       else if(windows == 3 && xd == sxdots)
	       {
		  RestoreRect(0, 0, xdots, ydots);
		  windows = 2;
	       }
	       break;
#ifdef XFRACT
	    case ENTER:
		break;
#endif
	    case '0':
	    case '1':
	    case '2':
/*	    case '3': */  /* don't use '3', it's already meaningful */
	    case '4':
	    case '5':
	    case '6':
	    case '7':
	    case '8':
	    case '9':
	       if (which == JIIM)
	       {
		  SecretExperimentalMode = kbdchar - '0';
		  break;
	       }
	    default:
	       still = 0;
	    }  /* switch */
	    if(kbdchar == 's' || kbdchar == 'S')
	       goto finish;
            if(dxcrsr > 0 || dycrsr > 0)
               exact = 0;
            xcrsr += dxcrsr;
            ycrsr += dycrsr;
            
	    /* keep cursor in logical screen */
	   if(xcrsr >= xdots)
	      xcrsr = xdots -1, exact = 0;
	   if(ycrsr >= ydots)
	      ycrsr = ydots -1, exact = 0;
	   if(xcrsr < 0)
	      xcrsr = 0, exact = 0;
	   if(ycrsr < 0)
	      ycrsr = 0, exact = 0;
	    
            Cursor_SetPos(xcrsr,ycrsr);
	 }  /* end while (keypressed) */

         if(exact == 0)
         {
	    if(integerfractal)
	    {
	       cr = lx0[xcrsr]+lx1[ycrsr];  /* supports rotated zoom boxes! */
	       ci = ly0[ycrsr]+ly1[xcrsr];
	       cr /= (1L<<bitshift);
	       ci /= (1L<<bitshift);
	    }
	    else
	    {
	       cr = dx0[xcrsr]+dx1[ycrsr];
	       ci = dy0[ycrsr]+dy1[xcrsr];
	    }
	 }
	 actively_computing = 1;
	 if(show_numbers) /* write coordinates on screen */
	 {
	    char str[80];
	    sprintf(str,"x=%16.14f y=%16.14f   ",cr,ci);
	    str[40] = 0;
	    if(windows == 0)
	    {
	       Cursor_Hide();
	       actively_computing = 1;
	       showtempmsg(str);
	       Cursor_Show();
	    }
	    else
	       displays(5, sydots-show_numbers, WHITE, BLACK, str,strlen(str));
	 }
	 iter = 1;
	 old.x = old.y = 0;
	 SaveC.x = init.x =  cr;
	 SaveC.y = init.y =  ci;
	 old_x = old_y = -1;
/*
 * MIIM code:
 * compute fixed points and use them as starting points of JIIM
 */
	 if (which == JIIM && OKtoMIIM)
	 {
	    _CMPLX f1, f2, Sqrt;	/* Fixed points of Julia */

	    Sqrt = ComplexSqrtFloat(1 - 4 * cr, -4 * ci);
	    f1.x = (1 + Sqrt.x) / 2;
	    f2.x = (1 - Sqrt.x) / 2;
	    f1.y =  Sqrt.y / 2;
	    f2.y = -Sqrt.y / 2;

	    ClearQueue();
	    maxhits = 1;
	    EnQueueFloat(f1.x, f1.y);
	    EnQueueFloat(f2.x, f2.y);
	 }
/*
 * End MIIM code.
 */
	 if(which == ORBIT)
           do_fractal_routines(cr, ci,PER_PIXEL);
	 /* move window if bumped */
	 if(windows==0 && xcrsr>xc && xcrsr < xc+xd && ycrsr>yc && ycrsr < yc+yd)
	 {
	    RestoreRect(xc,yc,xd,yd);
	    if (xc == xd*2)
	       xc = 2;
	    else
	       xc = xd*2;
	    xoff = xc + xd /  2;
	    SaveRect(xc,yc,xd,yd);
	 }
	 if(windows == 2)
	 {
	    fillrect(xdots, yc, xd-xdots, yd-show_numbers, color_dark);
	    fillrect(xc   , ydots, xdots, yd-ydots-show_numbers, color_dark);
	 }
	 else
	    fillrect(xc, yc, xd, yd, color_dark);

      } /* end if (keypressed) */

      if(which == JIIM)
      {
         if(hasinverse == 0)
            continue;
/*
 * MIIM code:
 * If we have MIIM queue allocated, then use MIIM method.
 */
	 if (OKtoMIIM)
	 {
	    if (QueueEmpty())
	    {
	       if (maxhits < colors - 1 && maxhits < 5 &&
		  (luckyx != 0.0 || luckyy != 0.0))
	       {
		  int i;

		  lsize  = lmax   = 0;
		  old.x  = new.x  = luckyx;
		  old.y  = new.y  = luckyy;
		  luckyx = luckyy = 0.0;
		  for (i=0; i<199; i++)
		  {
		     old = ComplexSqrtFloat(old.x - cr, old.y - ci);
		     new = ComplexSqrtFloat(new.x - cr, new.y - ci);
		     EnQueueFloat( new.x,  new.y);
		     EnQueueFloat(-old.x, -old.y);
		  }
		  maxhits++;
	       }
	       else
		  continue;		/* loop while (still) */
	    }

	    old = DeQueueFloat();

#if 0 /* try a different new method */
	    if (lsize < (lmax / 8) && maxhits < 5)	/* NEW METHOD */
	       if (maxhits < colors - 1)
		   maxhits++;
#endif
	    x = old.x * xfactor * zoom + xoff;
	    y = old.y * yfactor * zoom + yoff;
	    color = c_getcolor(x, y);
	    if (color < maxhits)
	    {
	       c_putcolor(x, y, color + 1);
	       new = ComplexSqrtFloat(old.x - cr, old.y - ci);
	       EnQueueFloat( new.x,  new.y);
	       EnQueueFloat(-new.x, -new.y);
	    }
	 }
	 else
	 {
/*
 * end Msnyder code, commence if not MIIM code.
 */
	 old.x -= cr;
	 old.y -= ci;
	 r = old.x*old.x + old.y*old.y;
	 if(r > 10.0)
	 {
	     old.x = old.y = 0.0; /* avoids math error */
	     iter = 1;
	 }
	 iter++;
	 color = ((count++)>>5)&(colors-1); /* chg color every 32 pts */
	 if(color==0)
	  color = 1;

	 r = sqrt(old.x*old.x + old.y*old.y);
	 new.x = sqrt(fabs((r + old.x)/2));
	 if (old.y < 0)
	    new.x = -new.x;

	 new.y = sqrt(fabs((r - old.x)/2));


	 switch (SecretExperimentalMode) {
	    case 0:			/* unmodified random walk */
	    default:
		if (rand() % 2)
		{
		   new.x = -new.x;
		   new.y = -new.y;
		}
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		break;
	    case 1:		  	/* always go one direction */
		if (SaveC.y < 0)
		{
		   new.x = -new.x;
		   new.y = -new.y;
		}
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		break;
	    case 2:			/* go one dir, draw the other */
		if (SaveC.y < 0)
		{
		   new.x = -new.x;
		   new.y = -new.y;
		}
		x = -new.x * xfactor * zoom + xoff;
		y = -new.y * yfactor * zoom + yoff;
		break;
	    case 4:			/* go negative if max color */
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		if (c_getcolor(x, y) == colors - 1)
		{
		   new.x = -new.x;
		   new.y = -new.y;
		   x = new.x * xfactor * zoom + xoff;
		   y = new.y * yfactor * zoom + yoff;
		}
		break;
	    case 5:			/* go positive if max color */
		new.x = -new.x;
		new.y = -new.y;
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		if (c_getcolor(x, y) == colors - 1)
		{
		   x = new.x * xfactor * zoom + xoff;
		   y = new.y * yfactor * zoom + yoff;
		}
		break;
	    case 7:
		if (SaveC.y < 0)
		{
		   new.x = -new.x;
		   new.y = -new.y;
		}
		x = -new.x * xfactor * zoom + xoff;
		y = -new.y * yfactor * zoom + yoff;
		if(iter > 10)
		{
		   if(mode == 0)			/* pixels  */
		      c_putcolor(x, y, color);
		   else if (mode & 1)            /* circles */
		   {
		      xbase = x;
		      ybase = y;
		      circle((int)(zoom*(xd >> 1)/iter),color);
		   }
		   if ((mode & 2) && x > 0 && y > 0 && old_x > 0 && old_y > 0)
		   {
		      draw_line(x, y, old_x, old_y, color);
		   }
		   old_x = x;
		   old_y = y;
		}
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		break;
	    case 8:			/* go in long zig zags */
		if (rancnt >= 300)
		    rancnt = -300;
		if (rancnt < 0)
		{
		    new.x = -new.x;
		    new.y = -new.y;
		}
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		break;
	    case 9:			/* "random run" */
		switch (randir) {
		    case 0:             /* go random direction for a while */
			if (rand() % 2)
			{
			    new.x = -new.x;
			    new.y = -new.y;
			}
			if (++rancnt > 1024)
			{
			    rancnt = 0;
			    if (rand() % 2)
				randir =  1;
			    else
				randir = -1;
			}
			break;
		    case 1:             /* now go negative dir for a while */
			new.x = -new.x;
			new.y = -new.y;
			/* fall through */
		    case -1:            /* now go positive dir for a while */
			if (++rancnt > 512)
			    randir = rancnt = 0;
			break;
		}
		x = new.x * xfactor * zoom + xoff;
		y = new.y * yfactor * zoom + yoff;
		break;
	 } /* end switch SecretMode (sorry about the indentation) */
	 } /* end if not MIIM */
      }
      else /* orbits */
      {
	 if(iter < maxit)
	 {
	    color = iter&(colors-1);
	    x = (old.x - init.x) * xfactor * 3 * zoom + xoff;
	    y = (old.y - init.y) * yfactor * 3 * zoom + yoff;
	    if(do_fractal_routines(cr, ci,ORBITCALC))
	       iter = maxit;
	    else
	       iter++;
	 }
	 else
	 {
	    x = y = -1;
	    actively_computing = 0;
	 }
      }
      if(which == ORBIT || iter > 10)
      {
	 if(mode == 0)			/* pixels  */
	    c_putcolor(x, y, color);
	 else if (mode & 1)            /* circles */
	 {
	    xbase = x;
	    ybase = y;
	    circle((int)(zoom*(xd >> 1)/iter),color);
	 }
	 if ((mode & 2) && x > 0 && y > 0 && old_x > 0 && old_y > 0)
	 {
	    draw_line(x, y, old_x, old_y, color);
	 }
	 old_x = x;
	 old_y = y;
      }
      old = new;
   } /* end while(still) */
finish:

/*
 * Msnyder code:
 * free MIIM queue
 */

   Free_Queue();
/*
 * end Msnyder code.
 */

   if(kbdchar != 's'&& kbdchar != 'S')
   {
      Cursor_Hide();
      if(windows == 0)
	 RestoreRect(xc,yc,xd,yd);
      else if(windows >= 2 )
      {
	 if(windows == 2)
	 {
	    fillrect(xdots, yc, xd-xdots, yd, color_dark);
	    fillrect(xc   , ydots, xdots, yd-ydots, color_dark);
	 }
	 else
	    fillrect(xc, yc, xd, yd, color_dark);
         if(windows == 3 && xd == sxdots) /* unhide */
         {
            RestoreRect(0, 0, xdots, ydots);
            windows = 2;
         }
	 Cursor_Hide();
	 savehasinverse = hasinverse;
	 hasinverse = 1;
	 SaveRect(0,0,xdots,ydots);
	 sxoffs = oldsxoffs;
	 syoffs = oldsyoffs;
	 RestoreRect(0,0,xdots,ydots);
	 hasinverse = savehasinverse;
      }
   }
   Cursor_Destroy();
#ifdef XFRACT
   Cursor_EndMouseTracking();
#endif
   delete(line_buff);
   if (memory)			/* done with memory, free it */
   {
      farmemfree(memory);
      memory = NULL;
   }

   lookatmouse = oldlookatmouse;
   using_jiim = 0;
   calctype = oldcalctype;

   helpmode = oldhelpmode;
   if(kbdchar == 's' || kbdchar == 'S')
   {
      viewwindow = viewxdots = viewydots = 0;
      viewreduction = 4.2;
      viewcrop = 1;
      finalaspectratio = screenaspect;
      xdots = sxdots;
      ydots = sydots;
      dxsize = xdots - 1;
      dysize = ydots - 1;
      sxoffs = 0;
      syoffs = 0;
      freetempmsg();
   }
   else
      cleartempmsg();
   if (file != NULL)
      {
      fclose(file);
      file = NULL;
      remove(scrnfile);
      }
   show_numbers = 0;
   ungetakey(kbdchar);
   EXIT_OVLY;
}
  
