/* Micro Cornucopia Magazine Issue #49
   PostScriptal article code

   PS-FR386.C -- 386-specific, fixed point Mandelbrot generator for PostScript
   file generation.

   This code generates four files comprising a four-color separation, all set
   for your favorite frinting press. I should say *my* favorite press. The
   output is:

   - right reading
   - emulsion side down
   - negative images with the following color layer assignments:

     layer      color        output file
     -------------------------------------
       1        yellow        frac1.ps
       2        magenta       frac2.ps
       3        cyan          frac3.ps
       4        black         frac4.ps

   PS-FR386.C is a heavily butchered version of Harlan Stockman's FR386.C.
   (See Micro Cornucopia Issue #43.) So give Harlan credit for the good stuff,
   and blame the weirdnesses on me.

   Everything's hard coded for the cover of Micro C. As usual, I've built no
   user interface.

   Compiled with DeSmet C88. Requires the assembly file, MAND.A.

   Larry Fogg - July, 1989

*/

#define maxc 750                           /* image size in columns and rows */
#define maxr 1200
#define maxcolors 82   /* number of colors defined - could be up to 64K here */
#define matx "750 "                           /* ASCII version of image size */
#define maty "300 "
#define bits "4"  /* # bits per point - ie 16 possible shades per point here */
#define str_length "375 "     /* 750 points @ 4 bits/point -> 375 bytes/line */
#define scr_lines "150 "                /* # lines per inch on output device */
#define num_copies "1"

#include <stdio.h>

int switchpt, i, itermax, mcol, mrow;
double xmin, xmax, ymin, ymax, newdy, newdx, dx, dy, fabs();
char key;
int mandel();
void mark_page (), do_crops ();
void file_header (), file_tailer (), draw_points (), get_layers ();
FILE *fp1, *fp2, *fp3, *fp4;

main()
{
  if(!is_386()) puts("SORRY! Need Intel 386!\n"), exit(0);

  mcol = maxc, mrow = maxr;

  puts ("80386 fixed point PostScript Mandelbrot set generator.\n\n");
  xmin=-0.7504, xmax=-0.7408, ymin=0.105, ymax=0.12036;
  itermax=2000, switchpt=200;  /* bug in DeSmet requires < 4 comma phrases */
  printf ("Press 'Q' to quit...\n");

  fp1 = fopen ("frac1.ps", "w");                             /* yellow layer */
  fp2 = fopen ("frac2.ps", "w");                            /* magenta layer */
  fp3 = fopen ("frac3.ps", "w");                               /* cyan layer */
  fp4 = fopen ("frac4.ps", "w");                              /* black layer */
  file_header (fp1);                                  /* defs and procedures */
  file_header (fp2);
  file_header (fp3);
  file_header (fp4);
  if ('q' == mandel (xmin, xmax, ymin, ymax, mcol, mrow))    /* get hex data */
    exit(0);
  file_tailer (fp1);              /* finish main part of PostScript programs */
  file_tailer (fp2);
  file_tailer (fp3);
  file_tailer (fp4);

  puts ("\n\nLast PostScriptal:\n");
  printf ("x= %.5f to %.5f\ny= %.5f to %.5f\nswitchpt= %d, iter= %d",
           xmin,xmax,ymin,ymax,switchpt,itermax);
  printf ("\nOutput files = FRAC(1C,2C,3C,4C).PS\n");
  fclose (fp1);
  fclose (fp2);
  fclose (fp3);
  fclose (fp4);
}  /* main */



/* mandel() IS ANALOGOUS TO L. FOGG'S VERSION IN DEC/JAN 87/88 MicroC ... */
/* ncol AND nrow ARE THE NUMBER OF COLUMNS AND ROWS THAT CAN BE DISPLAYED */
/* endcolor IS ONE LESS THAN MAXIMUM # COLORS DEFINED. nmax IS THE */
/* MAXIMUM NUMBER OF ITERATIONS WE WILL ALLOW FOR MANDELBROT SERIES... */
/* DeSmet FUNCTION scr_csts() READS KEYBOARD BUF, BUT DOESN'T WAIT IF NO KEY */
/* WAS PRESSED ... SIMILAR TO Turbo C COMBINATION OF kbhit() AND getch()... */

mandel (Pmin, Pmax, Qmin, Qmax, ncol, nrow)
double Pmin, Pmax, Qmin, Qmax;
int ncol, nrow;
{
  int row, col, iter, mandwhile();
  int point1, point2;
  long P, Q, P0, Q0, dP, dQ;
  long muldiv();

  dP = (Pmax-Pmin)*16777216 + 0.5;               /* 16777216 = 2 to the 24th */
  dQ = (Qmax-Qmin)*16777216 + 0.5;
  P0 = Pmin*16777216 + 0.5;
  Q0 = Qmin*16777216 + 0.5;

  for (row=0; row<nrow; ++row)
  {
    printf ("%d rows of %d\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", row, mrow);
    for (col=ncol-1; col>=0; --col) /* backwards to make emulsion side down */
    {
      P = P0 + muldiv (dP, col, ncol-1);
      Q = Q0 + muldiv (dQ, row, nrow-1);
      iter = mandwhile(P, Q, itermax) + 1;  /*+1 DEPENDS ON HOW 1ST IT DEF'ED*/
      if ((col %2) == 1)            /* this is the first of a pair of points */
        point1 = iter;
      else
      {
        point2 = iter;                     /* this is the second of the pair */
        draw_points (point1, point2); /*draw the pair (generate one hex byte)*/
      }
    }
    if (scr_csts()=='q') return ('q');                        /* wanna quit? */
  }
}  /* mandel */





void draw_points (pt1, pt2)
int pt1, pt2;
{
  int transform();
  unsigned char color;
  unsigned char yellow, magenta, cyan, black;
  unsigned char y_temp, m_temp, c_temp, b_temp;

  yellow = magenta = cyan = black = 0;
  y_temp = m_temp = c_temp = b_temp = 0;
  if (pt1 == itermax + 1)                      /* it's in the Mandelbrot set */
  {                /* these color assignments draw the point in a deep black */
    cyan = 0x90;    /* assignments made to high order nybble for first point */
    yellow = 0x30;
    black = 0xf0;
  }
  else                                                   /* point not in set */
  {
    color = transform (pt1, switchpt) % maxcolors;              /* get color */
    get_layers (color, &y_temp, &m_temp, &c_temp, &b_temp); /*seperate layers*/
    yellow = y_temp << 4;         /* shift assignments to high order nybbles */
    magenta = m_temp << 4;
    cyan = c_temp << 4;
    black = b_temp << 4;
  }
  y_temp = m_temp = c_temp = b_temp = 0;
  if (pt2 == itermax + 1)         /* do point 2 - it's in the Mandelbrot set */
  {                                                         /* make it black */
    cyan += 0x09;                            /* low order nybble for point 2 */
    yellow += 0x03;
    black += 0x0f;
  }
  else                                             /* point 2 not in the set */
  {
    color = transform (pt2, switchpt) % maxcolors;
    get_layers (color, &y_temp, &m_temp, &c_temp, &b_temp);
    yellow += y_temp;
    magenta += m_temp;
    cyan += c_temp;
    black += b_temp;
  }

  /* screwed with the color assignments here. Changing them made for a nicer
     looking cover.

     magenta data --> yellow layer
     cyan data    --> magenta layer
     yellow data  --> cyan layer
     black        --> black layer
  */

  if (yellow < 0x10)      /* pad single digit (hexit) numbers with leading 0 */
    fprintf (fp3, "0");
  if (magenta < 0x10)
    fprintf (fp1, "0");
  if (cyan < 0x10)
    fprintf (fp2, "0");
  if (black < 0x10)
    fprintf (fp4, "0");
  fprintf (fp3, "%x", yellow); /*write the 2 point hex values to the 4 layers*/
  fprintf (fp1, "%x", magenta);
  fprintf (fp2, "%x", cyan);
  fprintf (fp4, "%x", black);
}  /* draw_points */





void do_crops (fp)        /* crop marks on 8.5 X 11 paper, .5" in from edges */
FILE *fp;
{
  fprintf (fp, "\nnewpath\n");                       /* clear existing paths */
  fprintf (fp, "0.01 inch setlinewidth\n");         /* set narrow line width */

  fprintf (fp, "1 inch 0.9 inch moveto\n");                    /* lower left */
  fprintf (fp, "0 inch -0.5 inch rlineto\n");
  fprintf (fp, "0.9 inch 1 inch moveto\n");
  fprintf (fp, "-0.5 inch 0 inch rlineto\n");

  fprintf (fp, "7.5 inch 0.9 inch moveto\n");                 /* lower right */
  fprintf (fp, "0 inch -0.5 inch rlineto\n");
  fprintf (fp, "7.6 inch 1 inch moveto\n");
  fprintf (fp, "0.5 inch 0 inch rlineto\n");

  fprintf (fp, "7.5 inch 10.1 inch moveto\n");                /* upper right */
  fprintf (fp, "0 inch 0.5 inch rlineto\n");
  fprintf (fp, "7.6 inch 10 inch moveto\n");
  fprintf (fp, "0.5 inch 0 inch rlineto\n");

  fprintf (fp, "1 inch 10.1 inch moveto\n");                   /* upper left */
  fprintf (fp, "0 inch 0.5 inch rlineto\n");
  fprintf (fp, "0.9 inch 10 inch moveto\n");
  fprintf (fp, "-0.5 inch 0 inch rlineto\n");

  fprintf (fp, "stroke\n");
}  /* do_crops */





void mark_page (fp)                /* make identifying marks on the 4 layers */
FILE *fp;
{
  fprintf (fp, "\nnewpath\n");                       /* clear existing paths */
  fprintf (fp, "0.05 inch setlinewidth\n");           /* set wide line width */
  fprintf (fp, "2 inch 0.9 inch moveto\n");              /* layer 1, 2, 3, 4 */
  fprintf (fp, "0 inch -0.2 inch rlineto\n");
  if ((fp==fp2) || (fp==fp3) || (fp==fp4))                  /* layer 2, 3, 4 */
  {
    fprintf (fp, "2.1 inch 0.9 inch moveto\n");
    fprintf (fp, "0 inch -0.2 inch rlineto\n");
  }
  if ((fp==fp3) || (fp==fp4))                                  /* layer 3, 4 */
  {
    fprintf (fp, "2.2 inch 0.9 inch moveto\n");
    fprintf (fp, "0 inch -0.2 inch rlineto\n");
  }
  if (fp==fp4)                                                    /* layer 4 */
  {
    fprintf (fp, "2.3 inch 0.9 inch moveto\n");
    fprintf (fp, "0 inch -0.2 inch rlineto\n");
  }
  fprintf (fp, "stroke\n");
}  /* mark_page */




void file_header (f)
FILE *f;
{
  fprintf (f, "/inch {72 mul} def\n");              /* define inch procedure */
  do_crops (f);
  mark_page (f);
  fprintf (f, scr_lines);                       /* set scr_lines line screen */
  if (f == fp1)                           /* yellow - 90 degree screen angle */
    fprintf (f, "90 ");
  if (f == fp2)                          /* magenta - 75 degree screen angle */
    fprintf (f, "75 ");
  if (f == fp3)                            /* cyan - 105 degree screen angle */
    fprintf (f, "105 ");
  if (f == fp4)                           /* black -  45 degree screen angle */
    fprintf (f, "45 ");
  /* obscure spot function */
  fprintf (f, "{dup mul exch dup mul add 1 exch sub} setscreen\n");
  fprintf (f, "/picstr ");                           /* bytes per image line */
  fprintf (f, str_length);
  fprintf (f, "string def\n");
  fprintf (f, "/fractal\n");                     /* define fractal procedure */
  fprintf (f, "{");
  fprintf (f, matx);                                           /* image size */
  fprintf (f, maty);
  fprintf (f, bits);
  fprintf (f, "[");                                 /* transformation matrix */
  fprintf (f, matx);
  fprintf (f, "0 0 ");
  fprintf (f, maty);
  fprintf (f, "0 0]\n");
  fprintf (f, "{currentfile picstr readhexstring pop}\n");/* read image data */
  fprintf (f, "image\n}def\n");
  fprintf (f, "1.75 inch 1.5 inch translate\n");         /* center the image */
  fprintf (f, "5 inch 8 inch scale\n");                   /* make it 5" x 8" */
  fprintf (f, "fractal\n");                    /* call the fractal procedure */
}  /* file_header */





void file_tailer (f)
FILE *f;
{
  fprintf (f, "\n/#copies ");                       /* set # copies to print */
  fprintf (f, num_copies);
  fprintf (f, " def\n");
  fprintf (f, "showpage\n");                               /* print the page */
  putc (0x04, f);                                          /* write a ^D EOF */
}  /* file_tailer */




void get_layers (clr, y, m, c, b)
unsigned char clr;
unsigned char *y, *m, *c, *b;
{
  switch (clr)
  {  /*for each of the 82 colors, assign value for each of the 4 color layers*/
    case 0: *m = 0x0f; *c = 0x0b; *b = 0x06;
            break;
    case 1: *m = 0x0d; *c = 0x0b; *b = 0x06;
            break;
    case 2: *m = 0x0c; *c = 0x0b; *b = 0x06;
            break;
    case 3: *m = 0x0b; *c = 0x0b; *b = 0x06;
            break;
    case 4: *m = 0x0a; *c = 0x0b; *b = 0x06;
            break;
    case 5: *m = 0x09; *c = 0x0b; *b = 0x06;
            break;
    case 6: *m = 0x08; *c = 0x0b; *b = 0x06;
            break;
    case 7: *m = 0x07; *c = 0x0b; *b = 0x06;
            break;
    case 8: *m = 0x06; *c = 0x0b; *b = 0x06;
            break;
    case 9: *m = 0x05; *c = 0x0b; *b = 0x06;
            break;
    case 10: *m = 0x04; *c = 0x0b; *b = 0x06;
             break;
    case 11: *m = 0x03; *c = 0x0b; *b = 0x06;
             break;
    case 12: *m = 0x02; *c = 0x0b; *b = 0x06;
             break;
    case 13: *m = 0x01; *c = 0x0b; *b = 0x06;
             break;
    case 14: *m = 0x00; *c = 0x0b; *b = 0x06;
             break;
    case 15: *c = 0x0b; *b = 0x05;
             break;
    case 16: *c = 0x0b; *b = 0x04;
             break;
    case 17: *c = 0x0b; *b = 0x03;
             break;
    case 18: *c = 0x0b; *b = 0x02;
             break;
    case 19: *c = 0x0b; *b = 0x01;
             break;
    case 20: *c = 0x0b; *b = 0x00;
             break;
    case 21: *c = 0x0b; *y = 0x01;
             break;
    case 22: *c = 0x0b; *y = 0x02;
             break;
    case 23: *c = 0x0b; *y = 0x03;
             break;
    case 24: *c = 0x0b; *y = 0x04;
             break;
    case 25: *c = 0x0b; *y = 0x05;
             break;
    case 26: *c = 0x0b; *y = 0x06;
             break;
    case 27: *c = 0x0b; *y = 0x07;
             break;
    case 28: *c = 0x0b; *y = 0x08;
             break;
    case 29: *c = 0x0b; *y = 0x09;
             break;
    case 30: *c = 0x0b; *y = 0x0a;
             break;
    case 31: *c = 0x0b; *y = 0x0b;
             break;
    case 32: *c = 0x0b; *y = 0x0c;
             break;
    case 33: *c = 0x0b; *y = 0x0d;
             break;
    case 34: *c = 0x0b; *y = 0x0e;
             break;
    case 35: *c = 0x0b; *y = 0x0f;
             break;
    case 36: *y = 0x0f; *c = 0x09;
             break;
    case 37: *y = 0x0f; *c = 0x08;
             break;
    case 38: *y = 0x0f; *c = 0x07;
             break;
    case 39: *y = 0x0f; *c = 0x06;
             break;
    case 40: *y = 0x0f; *c = 0x05;
             break;
    case 41: *y = 0x0f; *c = 0x04;
             break;
    case 42: *y = 0x0f; *c = 0x03;
             break;
    case 43: *y = 0x0f; *c = 0x02;
             break;
    case 44: *y = 0x0f; *c = 0x01;
             break;
    case 45: *y = 0x0f; *c = 0x00;
             break;
    case 46: *y = 0x0b; *m = 0x01; *b = 0x00;
             break;
    case 47: *y = 0x0b; *m = 0x01; *b = 0x01;
             break;
    case 48: *y = 0x0b; *m = 0x01; *b = 0x02;
             break;
    case 49: *y = 0x0b; *m = 0x02; *b = 0x02;
             break;
    case 50: *y = 0x0b; *m = 0x03; *b = 0x02;
             break;
    case 51: *y = 0x0b; *m = 0x04; *b = 0x02;
             break;
    case 52: *y = 0x0b; *m = 0x05; *b = 0x02;
             break;
    case 53: *y = 0x0b; *m = 0x06; *b = 0x02;
             break;
    case 54: *y = 0x0b; *m = 0x07; *b = 0x02;
             break;
    case 55: *y = 0x0b; *m = 0x08; *b = 0x02;
             break;
    case 56: *y = 0x0b; *m = 0x09; *b = 0x02;
             break;
    case 57: *y = 0x0b; *m = 0x0a; *b = 0x02;
             break;
    case 58: *y = 0x0b; *m = 0x0b; *b = 0x02;
             break;
    case 59: *y = 0x0b; *m = 0x0c; *b = 0x02;
             break;
    case 60: *y = 0x0b; *m = 0x0d; *b = 0x02;
             break;
    case 61: *y = 0x0b; *m = 0x0e; *b = 0x02;
             break;
    case 62: *y = 0x0b; *m = 0x0f; *b = 0x02;
             break;
    case 63: *y = 0x0f; *m = 0x0f; *c = 0x09;
             break;
    case 64: *y = 0x0f; *m = 0x0f; *c = 0x0a;
             break;
    case 65: *y = 0x0f; *m = 0x0f; *c = 0x0b;
             break;
    case 66: *y = 0x0f; *m = 0x0f; *c = 0x0c;
             break;
    case 67: *y = 0x0e; *m = 0x0f; *c = 0x0c;
             break;
    case 68: *y = 0x0d; *m = 0x0f; *c = 0x0c;
             break;
    case 69: *y = 0x0c; *m = 0x0f; *c = 0x0c;
             break;
    case 70: *y = 0x0b; *m = 0x0f; *c = 0x0c;
             break;
    case 71: *y = 0x0a; *m = 0x0f; *c = 0x0c;
             break;
    case 72: *y = 0x09; *m = 0x0f; *c = 0x0c;
             break;
    case 73: *y = 0x08; *m = 0x0f; *c = 0x0c;
             break;
    case 74: *y = 0x07; *m = 0x0f; *c = 0x0c;
             break;
    case 75: *y = 0x06; *m = 0x0f; *c = 0x0c;
             break;
    case 76: *y = 0x05; *m = 0x0f; *c = 0x0c;
             break;
    case 77: *y = 0x04; *m = 0x0f; *c = 0x0c;
             break;
    case 78: *y = 0x03; *m = 0x0f; *c = 0x0c;
             break;
    case 79: *y = 0x02; *m = 0x0f; *c = 0x0c;
             break;
    case 80: *y = 0x01; *m = 0x0f; *c = 0x0c;
             break;
    case 81: *y = 0x00; *m = 0x0f; *c = 0x0c;
             break;
  }
}  /* get_layers */
