From: astrolog@byron.u.washington.edu (Astrolog)
Newsgroups: alt.astrology
Subject: * Astrolog birthchart program version 2.30 [Part 08/12] graphics.c
Summary: (Scorpio) Source file - extract and save to "graphics.c"
Keywords: Astrolog 2.30
Date: 12 Sep 92 03:09:27 GMT
Organization: University of Washington

/*
** Astrolog (Version 2.30) File: graphics.c
** (Initially programmed 10/23-29/1991)
**
** IMPORTANT: the graphics database and chart display routines used in
** this program are Copyright (C) 1991-1992 by Walter D. Pullen. Permission
** is granted to freely use and distribute these routines provided one
** doesn't sell, restrict, or profit from them in any way. Modification
** is allowed provided these notices remain with any altered or edited
** versions of the program.
*/

#include "astrolog.h"

#ifdef GRAPH

typedef struct {               /* Data structure to store boolean bitmap. */
  byte m[BITMAPX][BITMAPY];
} bitmapstruct;
typedef bitmapstruct *bitmap;

/* Macros to access a point in bitmap or allocate one from free memory. */

#define P(B, X, Y) ((B)->m[X][Y])
#define allocate(B) B = (bitmapstruct *) malloc(sizeof(bitmapstruct))


/*
*******************************************************************************
** Variables
*******************************************************************************
*/

/* Names of colors used by the program when drawing in a color X window. You */
/* can change these if you prefer others or your computer can't find these.  */

char *maincolorname[]
  = {"black", "white", "gold", "gray35", "tan", "pink", "blue"};
char *rainbowcolorname[]
  = {"white", "orangered", "orange", "yellow", "green",
       "cyan", "deepskyblue", "violet"};

/* These arrays contain the color indexes of each element and aspect. On */
/* these are based all the other color values, e.g. planet colors, etc.  */

int elemcolorindex[]   = {-1,  4, -4, -6};
int aspectcolorindex[] = { 1, -3,  6, -1, -4, -5,  4,  4, -2, -2,
			  -7, -7, -7,  2,  5,  5,  2,  2,  5};

/* These are the actual color arrays and variables used by the program.      */
/* Technically, Astrolog always assumes we are drawning on a color terminal; */
/* for B/W graphics, all the values below are filled with black or white.    */

color maincolor[6+1], rainbowcolor[7+1],
  elemcolor[4], aspectcolor[ASPECTS+1], objectcolor[TOTAL+1], hilite, gray;

/* A bunch of physical X window variables dealing with the window itself. */

#ifdef X11
Colormap cmap;
Display *disp;
GC gc, pmgc;
KeySym key;
Pixmap pixmap, icon;
Window window, root;
XEvent event;
XSizeHints hint;
XWMHints *wmhint;
int screen, depth;
char xkey[10];
#endif
color fg, bg, on = ON, off = OFF;

/* Global variables used by the graphics routines. */

bitmap bm;
char modex = 'c', outputfile[STRING] = "tty", string[STRING];
#ifdef X11
int xbitmap = FALSE, xmono = FALSE, xcolor = TRUE;
#else
int xbitmap = TRUE,  xmono = TRUE,  xcolor = FALSE;
#endif
int xreverse = FALSE, xroot = FALSE, xtext = TRUE, bonus = FALSE, label = TRUE,
  turtlex = 0, turtley = 0, scale = 200, chartx = DEFAULTX, charty = DEFAULTY,
  xnow = FALSE, degree = 0;
double Mon1, Day1, Yea1, Tim1, Zon1, Lon1, Lat1, tilt = 0.0;

/* These macros are used by the 'o' and 'O' keys to save chart information. */

#define STORE1  Mon1 = Mon; Day1 = Day; Yea1 = Yea; Tim1 = Tim; Zon1 = Zon; \
  Lon1 = Lon; Lat1 = Lat
#define RECALL1 Mon = Mon1; Day = Day1; Yea = Yea1; Tim = Tim1; Zon = Zon1; \
  Lon = Lon1; Lat = Lat1


/*
*******************************************************************************
** Astrolog icon
*******************************************************************************
*/

#ifdef X11
/* This information used to define Astrolog's X icon (Rainbow over Third */
/* Eye) is identical to the output format used by the bitmap program.    */
/* You could extract this section and run xsetroot -bitmap on it.        */

#define icon_width 63
#define icon_height 32
static char icon_bits[] = {
 0x00,0x00,0x00,0xa8,0x0a,0x00,0x00,0x00,0x00,0x00,0x40,0x55,0x55,0x01,0x00,
 0x00,0x00,0x00,0xa8,0xaa,0xaa,0x0a,0x00,0x00,0x00,0x00,0x54,0xf5,0x57,0x15,
 0x00,0x00,0x00,0x80,0xaa,0xaa,0xaa,0xaa,0x00,0x00,0x00,0x40,0xd5,0xff,0xff,
 0x55,0x01,0x00,0x00,0xa0,0xaa,0xaa,0xaa,0xaa,0x02,0x00,0x00,0x50,0xfd,0xff,
 0xff,0x5f,0x05,0x00,0x00,0xa8,0xaa,0x2a,0xaa,0xaa,0x0a,0x00,0x00,0xd4,0xff,
 0xaf,0xfa,0xff,0x15,0x00,0x00,0xaa,0x2a,0x00,0x00,0xaa,0x2a,0x00,0x00,0xf5,
 0xbf,0xaa,0xaa,0xfe,0x57,0x00,0x80,0xaa,0x02,0x00,0x00,0xa0,0xaa,0x00,0x40,
 0xfd,0xab,0xfa,0xaf,0xea,0x5f,0x01,0xa0,0xaa,0x80,0xff,0xff,0x80,0xaa,0x02,
 0x50,0xff,0xea,0xff,0xff,0xab,0x7f,0x05,0xa0,0x2a,0xf0,0xff,0xff,0x07,0xaa,
 0x02,0xd0,0xbf,0xfa,0x0f,0xf8,0xaf,0x7e,0x05,0xa8,0x0a,0xfc,0x01,0xc0,0x1f,
 0xa8,0x0a,0xd4,0xaf,0x7e,0x00,0x00,0xbf,0xfa,0x15,0xa8,0x0a,0x3f,0x00,0x00,
 0x7e,0xa8,0x0a,0xf4,0xaf,0x1f,0xe0,0x03,0xfc,0xfa,0x15,0xaa,0x82,0x0f,0xdc,
 0x1d,0xf8,0xa0,0x2a,0xf4,0xab,0x07,0x23,0x62,0xf0,0xea,0x17,0xaa,0xc2,0x87,
 0x91,0xc4,0xf0,0xa1,0x2a,0xf4,0xeb,0xc3,0xd0,0x85,0xe1,0xeb,0x17,0xaa,0xe0,
 0x83,0x91,0xc4,0xe0,0x83,0x2a,0xf5,0xeb,0x03,0x23,0x62,0xe0,0xeb,0x57,0xaa,
 0xe0,0x01,0xdc,0x1d,0xc0,0x83,0x2a,0xf5,0xeb,0x01,0xe0,0x03,0xc0,0xeb,0x57,
 0xaa,0xe0,0x01,0x00,0x00,0xc0,0x83,0x2a,0xfd,0xeb,0x01,0x00,0x00,0xc0,0xeb,
 0x5f};
#endif


/*
*******************************************************************************
** Standard procedures
*******************************************************************************
*/

/* Assert that n1 <= n2, swapping the values if this is not the case. */

sort(n1, n2)
int *n1, *n2;
{
  register int temp;
  if (*n1 > *n2) {
    temp = *n1;
    *n1  = *n2;
    *n2  = temp;
  }
}


/* Convert a string segment to a positive number, updating the string to  */
/* point beyond the number chars. Return 1 if the string doesn't point to */
/* a numeric value. This is used by the turtle() routine to extract       */
/* motion vector quantities from draw strings, e.g. the "12" in "U12".    */

int intinstring(str)
char **str;
{
  int num = 0, i;
  for (i = 0; ; i++) {
    if (**str < '0' || **str > '9')
      return num > 0 ? num : (i < 1 ? 1 : 0);
    num = num*10+(**str)-'0';
    (*str)++;
  }
}


char bitmapmode = BITMAPMODE;

/* Output the bitmap in memory to a file. This basically consists of just    */
/* calling the routine to actually write a bitmap to a file, although we     */
/* need to prompt the user for a filename if it wasn't specified beforehand. */

writefile()
{
  FILE *data;
  int tty;
  tty = (outputfile[0] == 't' && outputfile[1] == 't' &&
	 outputfile[2] == 'y' && outputfile[3] == 0);
  while (TRUE) {
    if (tty) {
      printf("Enter name of file to write X bitmap to > ");
      if (gets(outputfile) == (char *) NULL) {
	printf("\nAstrolog terminated.\n");
	exit(2);
      }
    }
    data = fopen(outputfile, "w");
    if (data != NULL)
      break;
    else {
      printf("Bad output file.\n");
      tty = 1;
    }
  }
  if (bitmapmode != 'A')
    writebitmap(data, outputfile, bitmapmode);
  else
    writeascii(data, outputfile);
  fclose(data);
}


/*
*******************************************************************************
** Graphic procedures
*******************************************************************************
*/

/* Force a pair of coordinates to be within the bounds of the window. */

legalize(x, y)
int *x, *y;
{
  if (*x < 0)
    *x = 0;
  else if (*x >= chartx)
    *x = chartx-1;
  if (*y < 0)
    *y = 0;
  else if (*y >= charty)
    *y = charty-1;
}


/* Set a single point on the screen. This is the most basic graphic function */
/* and is called by all the more complex routines. Based on what mode we are */
/* in, we either set a cell in the bitmap array or a pixel on the window.    */

void drawpoint(x, y, o)
int x, y;
bit o;
{
  legalize(&x, &y);
  if (xbitmap)
    P(bm, x, y) = o;
#ifdef X11
  else
    XDrawPoint(disp, pixmap, gc, x, y);
#endif
}


/* Draw a filled in block, defined by the corners of its rectangle. */

void block(x1, y1, x2, y2, o)
int x1, y1, x2, y2;
bit o;
{
  int x, y;
  legalize(&x1, &y1); legalize(&x2, &y2);
  sort(&y1, &y2); sort(&x1, &x2);
  if (xbitmap)
    for (y = y1; y <= y2; y++)         /* For bitmap, it's faster to  */
      for (x = x1; x <= x2; x++)       /* just fill in the array.     */
	P(bm, x, y) = o;
  else
    for (y = y1; y <= y2; y++)         /* For X window, let's go call */
      drawline(x1, y, x2, y, o, 0);    /* XDrawLine for fewer events. */
}


/* Draw a rectangle on the screen with specified thickness. This is just */
/* like block() except that we are only drawing the edges of the area.   */

void rect(x1, y1, x2, y2, xsiz, ysiz, o)
int x1, y1, x2, y2, xsiz, ysiz;
bit o;
{
  block(x1, y1, x2, y1 + ysiz - 1, o);
  block(x1, y1 + ysiz, x1 + xsiz - 1, y2 - ysiz, o);
  block(x2 - xsiz + 1, y1 + ysiz, x2, y2 - ysiz, o);
  block(x1, y2 - ysiz + 1, x2, y2, o);
}


/* Draw a line on the screen, specified by its endpoints. In addition, we */
/* have specified a skip factor, which allows us to draw dashed lines.    */

void drawline(x1, y1, x2, y2, o, skip)
int x1, y1, x2, y2, skip;
bit o;
{
  int x = x1, y = y1, xadd, yadd, xinc, yinc, xabs, yabs, i, j = 0;
  if (skip < 0)
    skip = 0;
#ifdef X11
  if (!xbitmap) {
    Xcolor(o);

    /* For non-dashed X window lines, let's just have the Xlib do it for us. */

    if (!skip) {
      XDrawLine(disp, pixmap, gc, x1, y1, x2, y2);
      return;
    }
  }
#endif
  xadd = x2 - x1 >= 0 ? 1 : 3;
  yadd = y2 - y1 >= 0 ? 2 : 4;
  xabs = abs(x2 - x1);
  yabs = abs(y2 - y1);

  /* Technically what we're doing here is drawing a line which is more    */
  /* horizontal then vertical. We always increment x by 1, and increment  */
  /* y whenever a fractional variable passes a certain amount. For lines  */
  /* that are more vertical than horizontal, we just swap x and y coords. */

  if (xabs < yabs) {
    swapint(&xadd, &yadd);
    swapint(&xabs, &yabs);
  }
  xinc = xabs;
  yinc = xabs >> 1;
  if (!(xabs & 1) && xadd > 2)
    yinc--;
  for (i = 0; i <= xinc; i++) {
    if (j < 1)
      drawpoint(x, y, o);
    j = j < skip ? j+1 : 0;
    switch (xadd) {
    case 1: x++; break;
    case 2: y++; break;
    case 3: x--; break;
    case 4: y--; break;
    }
    yinc += yabs;
    if (yinc - xabs >= 0) {
      yinc -= xabs;
      switch (yadd) {
      case 1: x++; break;
      case 2: y++; break;
      case 3: x--; break;
      case 4: y--; break;
      }
    }
  }
}


/* This routine, and its companion below, clips a line defined by its */
/* endpoints to either above the y=0 line, or below some line y=c. By */
/* passing in parameters in different orders, we can clip to vertical */
/* lines, too. These are used by the drawclip() routine below.        */

clipnegative(x1, y1, x2, y2)
int *x1, *y1, *x2, *y2;
{
  *x1 -= *y1*(*x2-*x1)/(*y2-*y1);
  *y1 = 0;
}

clipgreater(x1, y1, x2, y2, s)
int *x1, *y1, *x2, *y2, s;
{
  *x1 += (s-*y1)*(*x2-*x1)/(*y2-*y1);
  *y1 = s;
}


/* Draw a line on the screen. This is just like drawline() routine earlier; */
/* however, first clip the endpoints to the window viewport before drawing. */

void drawclip(x1, y1, x2, y2, o, skip)
int x1, y1, x2, y2, skip;
bit o;
{
  if (x1 < 0)
    clipnegative(&y1, &x1, &y2, &x2);    /* Check left side of window. */
  if (x2 < 0)
    clipnegative(&y2, &x2, &y1, &x1);
  if (y1 < 0)
    clipnegative(&x1, &y1, &x2, &y2);    /* Check top side of window.  */
  if (y2 < 0)
    clipnegative(&x2, &y2, &x1, &y1);
  if (x1 > chartx)
    clipgreater(&y1, &x1, &y2, &x2, chartx);    /* Check right of window.  */
  if (x2 > chartx)
    clipgreater(&y2, &x2, &y1, &x1, chartx);
  if (y1 > charty)
    clipgreater(&x1, &y1, &x2, &y2, charty);    /* Check bottom of window. */
  if (y2 > charty)
    clipgreater(&x2, &y2, &x1, &y1, charty);
  drawline(x1, y1, x2, y2, o, skip);            /* Go draw the line.       */
}


/* This routine is used to draw complicated objects composed of lots of line */
/* segments on the screen, such as all the glyphs and coastline pieces. It   */
/* is passed in a string of commands defining what to draw in relative       */
/* coordinates. This is a copy of the format of the BASIC draw command found */
/* in PC's. For example, "U5R10D5L10" means go up 5 dots, right 10, down 5,  */
/* and left 10 - draw a box twice as wide as it is high.                     */

void turtle(lin, x0, y0, o)
char *lin;
int x0, y0;
bit o;
{
  int i, j, x, y, deltax, deltay, blank = FALSE, noupdate = FALSE;
  char cmd;
  turtlex = x0; turtley = y0;
#ifdef X11
  if (!xbitmap)
    Xcolor(o);
#endif
  while (cmd = CAP(*lin)) {
    lin++;
    switch (cmd) {

    /* 'B' prefixing a command means just move the cursor, and don't draw. */

    case 'B':
      blank = TRUE; break;

    /* 'N' prefixing a command means don't update cursor when done drawing. */

    case 'N':
      noupdate = TRUE; break;

    /* Here we process the eight directional commands. */

    case 'U': case 'D': case 'L': case 'R':
    case 'E': case 'F': case 'G': case 'H':
      switch (cmd) {
      case 'U': deltax =  0; deltay = -1; break;    /* Up    */
      case 'D': deltax =  0; deltay =  1; break;    /* Down  */
      case 'L': deltax = -1; deltay =  0; break;    /* Left  */
      case 'R': deltax =  1; deltay =  0; break;    /* Right */
      case 'E': deltax =  1; deltay = -1; break;    /* Northeast */
      case 'F': deltax =  1; deltay =  1; break;    /* Southeast */
      case 'G': deltax = -1; deltay =  1; break;    /* Southwest */
      case 'H': deltax = -1; deltay = -1; break;    /* Northwest */
      default: ;
      }
      x = turtlex;
      y = turtley;
      j = intinstring(&lin)*scale/100;    /* Figure out how far we draw. */
      if (blank) {
	turtlex += deltax*j;
	turtley += deltay*j;
      } else {
	drawpoint(turtlex, turtley, o);
	for (i = 0; i < j; i++) {
	  turtlex += deltax;
	  turtley += deltay;
	  drawpoint(turtlex, turtley, o);
	}
      }
      if (noupdate) {
	turtlex = x;
	turtley = y;
      }
      break;
    default:
      fprintf(stderr, "Bad turtle subcommand: '%c'\n", cmd);    /* Shouldn't */
      exit(1);                                                  /* happen.   */
    }
    if (cmd != 'B' && cmd != 'N')
      blank = noupdate = FALSE;
  }
}


#define INTTOHEX(I) (char) ((I) < 10 ? '0' + (I) : 'a' + (I) - 10)

/* Write the bitmap array to a previously opened file in a format that   */
/* can be read in by the unix X commands bitmap and xsetroot. The 'mode' */
/* parameter defines how much white space is put in the file.            */

writebitmap(data, name, mode)
FILE *data;
char *name, mode;
{
  int x, y, i, value, temp = 0;
  fprintf(data, "#define %s_width %d\n" , name, chartx);
  fprintf(data, "#define %s_height %d\n", name, charty);
  fprintf(data, "static %s %s_bits[] = {",
	  mode != 'S' ? "char" : "short", name);
  for (y = 0; y < charty; y++) {
    x = 0;
    do {

      /* Process each row, eight columns at a time. */

      if (y + x > 0)
        fprintf(data, ",");
      if (temp == 0)
      	fprintf(data, "\n%s",
		mode == 'B' ? "  " : (mode == 'C' ? " " : ""));
      value = 0;
      for (i = (mode != 'S' ? 7 : 15); i >= 0; i--)
        value = (value << 1) + (!P(bm, x + i, y) && x + i < chartx);
      if (mode == 'B')
	putc(' ', data);
      fprintf(data, "0x");
      if (mode == 'S')
        fprintf(data, "%c%c",
		INTTOHEX(value >> 12), INTTOHEX((value >> 8) & 15));
      fprintf(data, "%c%c",
	      INTTOHEX((value >> 4) & 15), INTTOHEX(value & 15));
      temp++;

      /* Is it time to skip to the next line while writing the file yet? */

      if ((mode == 'B' && temp >= 12) ||
	  (mode == 'C' && temp >= 15) ||
	  (mode == 'S' && temp >= 11))
      	temp = 0;
      x += (mode != 'S' ? 8 : 16);
    } while (x < chartx);
  }
  fprintf(data, "};\n");
}


/* Write the bitmap array to a previously opened file in a simple boolean    */
/* ascii rectangle, one char per pixel, where '#' represents an off bit and  */
/* '-' an on bit. The output format is identical to the format generated by  */
/* the unix bmtoa command, and it can be converted into a bitmap with atobm. */

writeascii(data)
FILE *data;
{
  int x, y;
  for (y = 0; y < charty; y++) {
    for (x = 0; x < chartx; x++)
      putc(P(bm, x, y) ? '-' : '#', data);
    putc('\n', data);
  }
}


/*
*******************************************************************************
** X routines
*******************************************************************************
*/

#ifdef X11
/* Allocate a color from the present colormap. Given a string like "red" or */
/* "blue" allocate this color and return a value specifying it.             */

color Xmakecolor(name)
char *name;
{
  XColor col;
  XParseColor(disp, cmap, name, &col);
  XAllocColor(disp, cmap, &col);
  return col.pixel;
}
#endif


/* Set up all the colors used by the program, i.e. the foreground and   */
/* background colors, and all the colors in the object arrays, based on */
/* whether or not we are in monochrome and/or reverse video mode.       */

Xcolorinit()
{
  int i, j;

  /* Let's assume everything is in black and white until we hear otherwise. */

  for (i = 1; i <= 7; i++)
    maincolor[i-1] = rainbowcolor[i] = !xreverse;
  maincolor[0] = rainbowcolor[0] = xreverse;
#ifdef X11
  if (!xbitmap) {

    /* If we are writing to a window, let's allocate the appropriate colors. */

    cmap = XDefaultColormap(disp, screen);
    for (i = 0; i <= 6; i++)
      maincolor[i] = Xmakecolor(maincolorname[xcolor ?
	(i > 1 ? i : (xreverse ? 1-i : i)) : (xreverse ? i < 1 : i > 0)]);
    for (i = 1; i <= 7; i++)
      rainbowcolor[i] = xcolor ?
	Xmakecolor(rainbowcolorname[i]) : maincolor[1];
  }
#endif

  /* Once the colors in the main color arrays are settled, we can fill in */
  /* everything else, e.g. the object color arrays, based on them.        */

  off = maincolor[0]; on = maincolor[1];
  hilite = maincolor[2]; gray = maincolor[3];
  for (i = 0; i < 4; i++)
    elemcolor[i] = (j = elemcolorindex[i]) < 0 ?
      rainbowcolor[-j] : maincolor[j];
  for (i = 0; i <= ASPECTS; i++)
    aspectcolor[i] = (j = aspectcolorindex[i]) < 0 ?
      rainbowcolor[-j] : maincolor[j];
  objectcolor[0] = elemcolor[1];
  for (i = 1; i <= 10; i++)
    objectcolor[i] = elemcolor[(ruler1[i]-1)%4];
  for (i = 11; i <= 15; i++)
    objectcolor[i] = hilite;
  for (i = 16; i <= 20; i++)
    objectcolor[i] = rainbowcolor[7];
  objectcolor[18] = elemcolor[1]; objectcolor[19] = elemcolor[0];
  objectcolor[21] = elemcolor[2]; objectcolor[22] = elemcolor[3];
  objectcolor[23] = elemcolor[1]; objectcolor[24] = elemcolor[2];
  for (i = U_LO; i <= U_HI; i++)
    objectcolor[i] = maincolor[5];
  for (i = S_LO; i <= S_HI; i++) {    /* Determine colors of the stars. */
    j = i-BASE;
    objectcolor[i] = (starbright[j] < 0.0 ? rainbowcolor[3] :
		      (starbright[j] < 1.0 ? hilite :
		       (starbright[j] < 2.0 && starbright[j] != 1.70 ?
			rainbowcolor[2] : rainbowcolor[1])));
  }
#ifdef X11
  if (!xbitmap) {
    XSetBackground(disp, gc, off);      /* Initialize X window colors. */
    XSetForeground(disp, pmgc, off);
    Xcolor(on);
  }
#endif
}

#ifdef X11
/* This routine opens up and initializes an X window and prepares it to be */
/* drawn upon, and gets various information about the display, too.        */

Xbegin()
{
  disp = XOpenDisplay(0);
  if (!disp) {
    printf("Can't open display.\n");
    exit(1);
  }
  screen = DefaultScreen(disp);
  bg = BlackPixel(disp, screen);
  fg = WhitePixel(disp, screen);
  hint.x = hint.width = chartx; hint.y = hint.height = charty;
  hint.min_width = BITMAPX1; hint.min_height = BITMAPY1;
  hint.max_width = BITMAPX;  hint.max_height = BITMAPY;
  hint.flags = PPosition | PSize | PMaxSize | PMinSize;
  /*wmhint = XGetWMHints(disp, window);
  wmhint->input = True;
  XSetWMHints(disp, window, wmhint);*/
  depth = DefaultDepth(disp, screen);
  if (depth < 5) {
    xmono = TRUE;      /* Is this a monochrome monitor? */
    xcolor = FALSE;
  }
  root = RootWindow(disp, screen);
  if (xroot)
    window = root;     /* If -XB in effect, we'll use the root window. */
  else
    window = XCreateSimpleWindow(disp, DefaultRootWindow(disp),
      hint.x, hint.y, hint.width, hint.height, 5, fg, bg);
  pixmap = XCreatePixmap(disp, window, chartx, charty, depth);
  icon = XCreateBitmapFromData(disp, DefaultRootWindow(disp),
    icon_bits, icon_width, icon_height);
  if (!xroot)
    XSetStandardProperties(disp, window, "Astrolog", "Astrolog",
      icon, xkey, 0, &hint);

  /* We have two graphics workareas. One is what the user currently sees in */
  /* the window, and the other is what we are currently drawing on. When    */
  /* done, we can quickly copy this to the viewport for a smooth look.      */

  gc = XCreateGC(disp, window, 0, 0);
  XSetGraphicsExposures(disp, gc, 0);
  pmgc = XCreateGC(disp, window, 0, 0);
  Xcolorinit();                            /* Go set up colors. */
  if (!xroot)
    XSelectInput(disp, window, KeyPressMask | StructureNotifyMask |
      ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask);
  XMapRaised(disp, window);
  XSync(disp, 0);
  XFillRectangle(disp, pixmap, pmgc, 0, 0, chartx, charty);
}


/* Add a certain amount of time to the current hour/day/month/year quantity */
/* defining the present chart. This is used by the chart animation feature. */
/* We can add or subtract anywhere from 1 to 9 seconds, minutes, hours,     */
/* days, months, years, decades, centuries, or millenia in any one call.    */
/* This is mainly just addition to the appropriate quantity, but we have    */
/* to check for overflows, e.g. Dec 30 + 3 days = Jan 2 of Current year + 1 */

addtime(mode, num)
int mode, num;
{
  double toadd, h, m, j;
  toadd = (double)num;
  h = floor(F);
  m = (F-floor(F))*100.0;
  if (mode == 1)
    m += 1.0/60.0*(double)toadd;    /* Add seconds. */
  else if (mode == 2)
    m += 1.0*(double)toadd;         /* add minutes. */

  /* Add hours, either naturally or if minute value overflowed. */

  if (m < 0.0 || m >= 60.0 || mode == 3) {
    if (m >= 60.0) {
      m -= 60.0; toadd = sgn(toadd);
    } else if (m < 0.0) {
      m += 60.0; toadd = sgn(toadd);
    }
    h += 1.0*(double)toadd;
  }

  /* Add days, either naturally or if hour value overflowed. */

  if (h >= 24.0 || h < 0.0 || mode == 4) {
    if (h >= 24.0) {
      h -= 24.0; toadd = sgn(toadd);
    } else if (h < 0.0) {
      h += 24.0; toadd = sgn(toadd);
    }
    D += 1.0*(double)toadd;
  }

  /* Add months, either naturally or if day value overflowed. */

  if (D > (j = (double) dayinmonth((int) M, (int) Y)) ||
      D < 1.0 || mode == 5) {
    if (D > j) {
      D -= j; toadd = sgn(toadd);
    } else if (D < 1.0) {
      D += j; toadd = sgn(toadd);
    }
    M += 1.0*(double)toadd;
  }

  /* Add years, either naturally or if month value overflowed. */

  if (M > 12.0 || M < 1.0 || mode == 6) {
    if (M > 12.0) {
      M -= 12.0; toadd = sgn(toadd);
    } else if (M < 1.0) {
      M += 12.0; toadd = sgn(toadd);
    }
    Y += 1.0*(double)toadd;
  }
  if (mode == 7)
    Y += 10.0*(double)toadd;      /* Add decades.   */
  else if (mode == 8)
    Y += 100.0*(double)toadd;     /* Add centuries. */
  else if (mode == 9)
    Y += 1000.0*(double)toadd;    /* Add millenia.  */
  F = h+m/100.0;                  /* Recalibrate hour time. */
}


/* Print a list of every key that one can press in an X window to do a */
/* certain function, and a description of what it does. This list gets */
/* displayed whenever one presses the 'H' or '?' key in the window.    */

displaykeys()
{
  printf("\nAstrolog window keypress options (version %s):\n", VERSION);
  printf(" Press 'H' or '?' to display this list of key options.\n");
  printf(" Press 'p' to toggle pause status on or off.\n");
  printf(" Press 'x' to toggle fg/bg colors in window.\n");
  printf(" Press 'm' to toggle color/monochrome display in window.\n");
  printf(" Press 'T' to toggle header info on current chart in window.\n");
  printf(" Press 'i' to toggle status of the minor chart modification.\n");
  printf(" Press 'l' to toggle labelling of object points in chart.\n");
  printf(" Press 'v' to display current chart in window on text screen.\n");
  printf(" Press 'R' to toggle restriction status of minor objects.\n");
  printf(" Press 'C' to toggle restriction status of minor house cusps.\n");
  printf(" Press 'u' to toggle restriction status of uranian planets.\n");
  printf(" Press 'U' to toggle restriction status of fixed stars.\n");
  printf(" Press 's', 'h', 'f', 'F' to toggle status of siderial zodiac,\n");
  printf("        heliocentric charts, domal charts, and decan charts.\n");
  printf(" Press 'O' and 'o' to recall/store a previous chart from memory.\n");
  printf(" Press 'B' to dump current window contents to root background.\n");
  printf(" Press 'Q' to resize wheel chart display to a square.\n");
  printf(" Press '<' and '>' to decrease/increase the scale size of the\n");
  printf("       glyphs and the size of world map.\n");
  printf(" Press '[' and ']' to decrease/increase tilt in globe display.\n");
  printf(" Press 'N' to toggle animation status on or off. Charts will\n");
  printf("       be updated to current status and globe will rotate.\n");
  printf(" Press '!'-'(' to begin updating current chart by adding times.\n");
  printf("       !: seconds, @: minutes, #: hours, $: days, %: months,\n");
  printf("       ^: years, &: years*10, *: years*100, (: years*1000.\n");
  printf(" Press 'r' to reverse direction of time-lapse or animation.\n");
  printf(" Press '1'-'9' to set rate of animation to 'n' degrees, etc.\n");
  printf(" Press 'V','L','A','Z','S','W','G','P' to switch to normal (-v),\n");
  printf("       astrograph (-L), grid (-g), local (-Z), space (-S),\n");
  printf("       world (-XW), globe (-XG), and polar (-XP) modes.\n");
  printf(" Press 'q' to terminate the window and program.\n\n");
  printf(" Left   mouse button: Draw lines on chart in window.\n");
  printf(" Middle mouse button: Print coordinates of pointer on world map.\n");
  printf(" Right  mouse button: Terminate the window and program.\n");
}


/* This routine gets called after an X window is brought up and displayed   */
/* on the screen. It loops, processing key presses, mouse clicks, etc, that */
/* the window receives, until the user specifies they want to exit program. */

Xspin()
{
  int xbreak = FALSE, xpause = FALSE, xupdate = FALSE, xevent = TRUE,
    xnew = FALSE, buttonx = 0, buttony = 0, dir = 1, length, i;
  double lon, lat;
  while (!xbreak) {

    /* First, make sure the window isn't too large or too small. */

    if (chartx < BITMAPX1) {
      chartx = BITMAPX1;
      xupdate = TRUE;
    } else if (chartx > BITMAPX) {
      chartx = BITMAPX;
      xupdate = TRUE;
    }
    if (charty < BITMAPY1) {
      charty = BITMAPY1;
      xupdate = TRUE;
    } else if (charty > BITMAPY) {
      charty = BITMAPY;
      xupdate = TRUE;
    }

    /* Some chart windows, like the world maps and aspect grids, should */
    /* always be a certian size, so correct if a resize was attempted.  */

    if (modex == 'l' || modex == 'w') {
      length = 360*scale/100+2;
      if (chartx != length) {
	chartx = length;
	xupdate = TRUE;
      }
      length = (90*2+1)*scale/100+2;
      if (charty != length) {
	charty = length;
	xupdate = TRUE;
      }
    } else if (modex == 'a') {
      if (chartx != (length = (OBJECTS+(relation==1))*CELLSIZE*scale/100+1)) {
	chartx = length;
	xupdate = TRUE;
      } if (charty != length) {
	charty = length;
	xupdate = TRUE;
      }
    }

    /* Physically resize window if we've changed the size parameters. */

    if (xupdate) {
      xupdate = FALSE;
      XResizeWindow(disp, window, chartx, charty);
      XFreePixmap(disp, pixmap);
      pixmap = XCreatePixmap(disp, window, chartx, charty, depth);
      xevent = TRUE;
    }

    /* Recast chart if the chart information has changed any. */

    if (xnew && (!xnow || xpause)) {
      xnew = FALSE;
      M = Mon; D = Day; Y = Yea; F = Tim; X = Zon; L5 = Lon; LA = Lat;
      castchart(TRUE);
      xevent = TRUE;
    }
    if (xnow && !xpause)
      xevent = TRUE;

    /* Update the window if anything has changed since last time round. */

    if (xevent) {
      xevent = FALSE;

      /* If we're in animation mode, change the chart info appropriately. */

      if (xnow)
	if (modex == 'w' || modex == 'g' || modex == 'p') {
	  degree += dir;
	  if (degree >= 360)      /* For animating globe display, add */
	    degree -= 360;        /* in appropriate degree value.     */
	  else if (degree < 0)
	    degree += 360;
	} else {
	  if (xnow == 10)
#ifdef TIME
	    /* For the continuous chart update to present moment */
	    /* animation mode, go get whatever time it is now.   */
	    inputdata("now")
#endif
	      ;
	  else {  /* Otherwise add on appropriate time vector to chart info. */
	    M = Mon; D = Day; Y = Yea; F = Tim; X = Zon; L5 = Lon; LA = Lat;
	    addtime(xnow, dir);
	  }
	  Mon = M; Day = D; Yea = Y; Tim = F; Zon = X; Lon = L5; Lat = LA;
	  castchart(TRUE);
	  if (relation) {
	    for (i = 1; i <= total; i++)    /* Make sure animations    */
	      planet2[i] = planet[i];       /* of relationship charts  */
	    for (i = 1; i <= SIGNS; i++)    /* are reflected properly. */
	      house2[i] = house[i];
	  }
	}            /* Update window display with new chart. */
      Xcolor(on);
      XFillRectangle(disp, pixmap, pmgc, 0, 0, chartx, charty);
      xchart();
      XSync(disp, 0);
      XCopyArea(disp, pixmap, window, gc, 0, 0, chartx, charty, 0, 0);
    }

    /* Now process what's on the event queue, i.e. any keys pressed, etc. */

    if (XEventsQueued(disp, QueuedAfterFlush /*QueuedAfterReading*/ )) {
      XNextEvent(disp, &event);

      /* Restore what's on window if a part of it gets uncovered. */

      if (event.type == Expose && event.xexpose.count == 0) {
	XSync(disp, 0);
	XCopyArea(disp, pixmap, window, gc, 0, 0, chartx, charty, 0, 0);
      }
      switch (event.type) {

      /* Check for a manual resize of window by user. */

      case ConfigureNotify:
	chartx = event.xconfigure.width;
	charty = event.xconfigure.height;
	XFreePixmap(disp, pixmap);
	pixmap = XCreatePixmap(disp, window, chartx, charty, depth);
	xevent = TRUE;
	break;
      case MappingNotify:
	XRefreshKeyboardMapping(&event);
	break;

      /* Process any mouse buttons the user pressed. */

      case ButtonPress:
	buttonx = event.xbutton.x; buttony = event.xbutton.y;
	if (event.xbutton.button == Button1) {
	  Xcolor(hilite);
	  drawpoint(buttonx, buttony, hilite);
	  XSync(disp, 0);
	  XCopyArea(disp, pixmap, window, gc, 0, 0, chartx, charty, 0, 0);
	} else if (event.xbutton.button == Button2 &&
		   (modex == 'l' || modex == 'w') && degree == 0) {
	  lon = 180.0-(double)(event.xbutton.x-1)/(double)(chartx-2)*DEGREES;
	  lat =  90.0-(double)(event.xbutton.y-1)/(double)(charty-2)*181.0;
	  printf("Mouse is at %s.\n", stringlocation(lon, lat, 60.0));
	} else if (event.xbutton.button == Button3)
	  xbreak = TRUE;
	break;

      /* Check for user dragging any of the mouse buttons across window. */

      case MotionNotify:
	Xcolor(hilite);
	drawline(buttonx, buttony, event.xbutton.x, event.xbutton.y,
		 hilite, 0);
	XSync(disp, 0);
	XCopyArea(disp, pixmap, window, gc, 0, 0, chartx, charty, 0, 0);
	buttonx = event.xbutton.x; buttony = event.xbutton.y;

      /* Process any keys user pressed in window. */

      case KeyPress:
	length = XLookupString(&event, xkey, 10, &key, 0);
	if (length == 1) {
	  i = xkey[0];
	  switch (i) {
	  case 'p':
	    xpause = 1-xpause;
	    break;
	  case 'r':
	    dir = -dir;
	    break;
	  case 'x':
	    xreverse = 1-xreverse;
	    Xcolorinit();
	    xevent = TRUE;
	    break;
	  case 'm':
	    if (!xmono) {
	      xcolor = 1-xcolor;
	      Xcolorinit();
	      xevent = TRUE;
	    }
	    break;
	  case 'B':
	    XSetWindowBackgroundPixmap(disp, root, pixmap);
	    XClearWindow(disp, root);
	    break;
	  case 'T':
	    xtext = 1-xtext;
	    xevent = TRUE;
	    break;
	  case 'i':
	    bonus = 1-bonus;
	    xevent = TRUE;
	    break;
	  case '<':
	    if (scale > 100) {
	      scale -= 100;
	      xupdate = TRUE;
	    }
	    break;
	  case '>':
	    if (scale < 300) {
	      scale += 100;
	      xupdate = TRUE;
	    }
	    break;
	  case '[':
	    if (modex == 'g') {
	      tilt = tilt > -90.0 ? tilt-11.25 : -90.0;
	      xevent = TRUE;
	    }
	    break;
	  case ']':
	    if (modex == 'g') {
	      tilt = tilt < 90.0 ? tilt+11.25 : 90.0;
	      xevent = TRUE;
	    }
	    break;
	  case 'Q':
	    if (chartx > charty)
	      chartx = charty;
	    else
	      charty = chartx;
	    xupdate = TRUE;
	    break;
	  case 'R':
	    for (i = 11; i <= 15; i++)
	      ignore[i] = 1-ignore[i];
	    ignore[17] = 1-ignore[17]; ignore[20] = 1-ignore[20];
	    xevent = TRUE;
	    break;
	  case 'C':
	    cusp = 1-cusp;
	    for (i = C_LO; i <= C_HI; i++)
	      ignore[i] = 1-ignore[i];
	    xnew = TRUE;
	    break;
	  case 'u':
	    uranian = 1-uranian;
	    for (i = U_LO; i <= U_HI; i++)
	      ignore[i] = 1-ignore[i];
	    xnew = TRUE;
	    break;
	  case 'U':
	    universe = universe ? 0 : 1;
	    for (i = S_LO; i <= S_HI; i++)
	      ignore[i] = 1-ignore[i];
	    xnew = TRUE;
	    break;
	  case 's':
	    siderial = 1-siderial;
	    xnew = TRUE;
	    break;
	  case 'h':
	    centerplanet = centerplanet ? 0 : 1;
	    xnew = TRUE;
	    break;
	  case 'f':
	    flip = 1-flip;
	    xnew = TRUE;
	    break;
	  case 'F':
	    decan = 1-decan;
	    xnew = TRUE;
	    break;
	  case 'o':
	    STORE1;
	    break;
	  case 'O':
	    RECALL1;
	    xnew = TRUE;
	    break;
	  case 'l':
	    label = 1-label;
	    xevent = TRUE;
	    break;
	  case 'N':                     /* The continuous update animation. */
	    xnow = xnow ? 0 : 10;
	    break;
	  case '!': xnow = 1; break;    /* These are the nine different     */
	  case '@': xnow = 2; break;    /* "add time to chart" animations.  */
	  case '#': xnow = 3; break;
	  case '$': xnow = 4; break;
	  case '%': xnow = 5; break;
	  case '^': xnow = 6; break;
	  case '&': xnow = 7; break;
	  case '*': xnow = 8; break;
	  case '(': xnow = 9; break;
	  case 'V': modex = 'c'; xevent = TRUE; break;    /* Should we go    */
	  case 'L': modex = 'l'; xevent = TRUE; break;    /* switch to a     */
	  case 'A': modex = 'a'; xevent = TRUE; break;    /* new chart type? */
	  case 'Z': modex = 'z'; xevent = TRUE; break;
	  case 'S': modex = 's'; xevent = TRUE; break;
	  case 'W': modex = 'w'; xevent = TRUE; break;
	  case 'P': modex = 'p'; xevent = TRUE; break;
	  case 'G': modex = 'g'; xevent = TRUE; break;
	  case 'v':
	    chartlocation(FALSE);
	    break;
	  case 'H': case '?':
	    displaykeys();
	    break;
	  case 'q':
	    xbreak = TRUE;
	    break;
	  default:
	    if (i > '0' && i <= '9')               /* Process numbers 1..9   */
	      dir = (dir > 0 ? 1 : -1)*(i-'0');    /* signifying anim. rate. */
	  }
	}
      default: ;
      }
    }
  }
}


/* This is called right before program termination to get rid of the window. */

Xend()
{
  XFreeGC(disp, gc);
  XFreeGC(disp, pmgc);
  XFreePixmap(disp, pixmap);
  XDestroyWindow(disp, window);
  XCloseDisplay(disp);
}
#endif


/*
*******************************************************************************
** Main processing
*******************************************************************************
*/


/* Print a list of every command switch dealing with the graphics features  */
/* that can be passed to the program, and a description of what it does.    */
/* This is part of what the -H switch prints, if graphics were compiled in. */

void xdisplayswitches()
{
  printf("\nSwitches to access graphics options:\n");
  printf(" -X: Create a graphics chart instead of displaying it as text.\n");
#ifdef X11
  printf(" -Xb: Create X bitmap instead of putting graphics in window.\n");
  printf(" -XB: Display X chart on root instead of in a separate window.\n");
  printf(" -Xm: For color monitors, display charts in black and white.\n");
#endif
  printf(" -Xr: Create window or bitmap in reversed colors (B on W).\n");
  printf(" -Xw <hor> [<ver>]: Change the size of the chart window.\n");
  printf(" -Xs <percentage>: Change the size of characters by n%%.\n");
  printf(" -Xi: Create chart in slightly modified form.\n");
  printf(" -XT: Inhibit display of chart info at bottom of window.\n");
  printf(" -XW: Simply create the bitmap of the world.\n");
  printf(" -XW0: Like -XW but don't just do rectangular projection.\n");
  printf(" -XP: Create the bitmap of the world, but as a polar projection.\n");
  printf(" -XG [<degrees>]: Display the bitmap of the world as a globe.\n");
#ifdef X11
  printf(" -Xn: Start up chart or globe display in animation mode.\n");
#endif
  printf(" -Xo <file>: Write output bitmap to specified file.\n");
#ifdef X11
  printf("Also, press 'H' within X window for list of key press options.\n");
#endif
}


/* Process a command line switch passed to the program dealing with the      */
/* graphics features. This is just like the processing of each switch in the */
/* main program; however, here each switch has been prefixed with an 'X'.    */

int xprocess(argc, argv, pos)
int argc, pos;
char **argv;
{
  int i = 0, j;
  switch (argv[0][pos]) {
  case 0:
    break;
  case 'W':
    if (argc > 1 && ((degree = atoi(argv[1])) || argv[1][0] == '0')) {
      i++;
      if (degree < 0 || degree > 359) {
	fprintf(stderr, "Value %d passed to switch -XW out of range.\n",
		degree);
	exit(1);
      }
    } else
      degree = 0;
    modex = 'w';
    if (argv[0][pos+1] == '0')
      todisplay |= DASHXW0;
    autom = TRUE;
    break;
  case 'P':
    if (argc > 1 && ((degree = atoi(argv[1])) || argv[1][0] == '0')) {
      i++;
      if (degree < 0 || degree > 359) {
	fprintf(stderr, "Value %d passed to switch -XP out of range.\n",
		degree);
	exit(1);
      }
    } else
      degree = 0;
    modex = 'p';
    autom = TRUE;
    break;
  case 'G':
    if (argc > 1 && ((degree = atoi(argv[1])) || argv[1][0] == '0')) {
      i++;
      if (degree < 0 || degree > 359) {
	fprintf(stderr, "Value %d passed to switch -XG out of range.\n",
		degree);
	exit(1);
      }
      if (argc > 2 && ((tilt = atof(argv[2])) || argv[2][0] == '0')) {
	i++;
	if (tilt < -90.0 || tilt > 90.0) {
	  fprintf(stderr, "Value %.1f passed to switch -XG out of range.\n",
		  tilt);
	  exit(1);
	}
      }
    }
    modex = 'g';
    autom = TRUE;
    break;
#ifdef X11
  case 'b':
    xbitmap = TRUE;
    xcolor = FALSE;
    break;
  case 'B':
    xroot = TRUE;
    break;
  case 'm':
    xcolor = FALSE;
    break;
#endif
  case 'r':
    xreverse = TRUE;
    break;
  case 'w':
    if (argc <= 1)
      toofew("Xw");
    chartx = atoi(argv[1]);
    if (argc > 2 && (charty = atoi(argv[2]))) {
      argc--; argv++;
      i++;
    } else
      charty = chartx;
    if (chartx < BITMAPY1 || chartx > BITMAPX ||
	charty < BITMAPY1 || charty > BITMAPY) {
      fprintf(stderr, "Values %d,%d passed to switch -Xw out of range.\n",
	      chartx, charty);
      exit(1);
    }
    i++;
    break;
  case 's':
    if (argc <= 1)
      toofew("Xs");
    scale = atoi(argv[1]);
    if (scale < 100 || scale > 300 || scale%100 != 0) {
      fprintf(stderr, "Value %d passed to switch -Xs out of range.\n", scale);
      exit(1);
    }
    i++;
    break;
  case 'i':
    bonus = TRUE;
    break;
  case 'T':
    xtext = FALSE;
    break;
#ifdef X11
  case 'n':
    if (argc > 1 && (xnow = atoi(argv[1])))
      i++;
    else
      xnow = 10;
    if (xnow < 1 || xnow > 10) {
      fprintf(stderr, "Value %d passed to switch -Xn out of range.\n", xnow);
      exit(1);
    }
    break;
#endif
  case 'o':
    if (argc <= 1)
      toofew("Xo");
    xbitmap = TRUE;
    xcolor = FALSE;
    for (j = 0; outputfile[j] = argv[1][j]; j++)
      ;
    i++;
    break;
  default:
    fprintf(stderr, "Unknown switch -X%c\n", argv[0][pos]);
    exit(1);
  }
  return i;    /* 'i' contains the value to be added to argc when we return. */
}


/* This is the main interface to all the graphics features. This routine     */
/* is called from the main program if any of the -X switches were specified, */
/* and it sets up for and goes and generates the appropriate graphics chart. */

void xaction()
{
  /* First figure out what graphic mode to generate the chart in, based on */
  /* various non-X command switches, e.g. -L combined with -X, -g combined */
  /* with -X, and so on, and determine the size the window is to be, too.  */

  if (modex == 'c' && (todisplay & DASHL) > 0)
    modex = 'l';
  else if (modex == 'c' && (todisplay & DASHG) > 0) {
    modex = 'a';
    chartx = charty = (OBJECTS+(relation==1))*CELLSIZE*scale/100+1;
  } else if (modex == 'c' && (todisplay & DASHZ) > 0) {
    modex = 'z';
    if (!(todisplay & DASHZ0)) {
      chartx = (360+12)*scale/100;
      charty = (90*2+12)*scale/100;
    }
  } else if (modex == 'c' && (todisplay & DASHS) > 0)
    modex = 's';
  if (modex == 'l' || modex == 'w') {
    chartx = 360*scale/100+2;
    charty = (90*2+1)*scale/100+2;
  }
  if (xbitmap) {     /* Initialize bitmap or window. */
    allocate(bm);
    Xcolorinit();
  }
#ifdef X11
  else
    Xbegin();
#endif
  if (xroot || xbitmap)    /* Go draw the graphic chart. */
    xchart();
  if (xbitmap) {    /* Write bitmap to file if in that mode. */
    writefile();
    free(bm);
  }
#ifdef X11
  else {
    if (xroot) {
      XSetWindowBackgroundPixmap(disp, root, pixmap);    /* Process -XB. */
      XClearWindow(disp, root);
    } else {
      STORE1;
      Xspin();    /* Window's up; process commands given to window now. */
    }
    Xend();
  }
#endif
}
#endif

/**/
Newsgroups: alt.astrology