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

/*
** Astrolog (Version 2.30) File: xcharts.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

#define FONTX 6
#define FONTY 10

/* Are particular coordinates on the chart? */
#define ISLEGAL(X, Y) \
  ((X) >= 0 && (X) < chartx && (Y) >= 0 && (Y) < chartx)

/* draw a rectangle with thin sides. */
#define edge(X1, Y1, X2, Y2, O) \
  rect(X1, Y1, X2, Y2, 1, 1, O)

/* draw a border around the whole window. */
#define rectall(XSIZ, YSIZ, O) \
  rect(0, 0, chartx-1, charty-1, XSIZ, YSIZ, O)

char string[STRING];
double symbol[TOTAL+1], circx[360+1], circy[360+1];


/*
*******************************************************************************
** Main subprograms
*******************************************************************************
*/

/* Return whether the specified object should be displayed in the current  */
/* graphics chart type. For example, don't include the Moon in the solar   */
/* system charts, don't include house cusps in astro-graph, and so on.     */

int proper(i)
int i;
{
  int j = TRUE;
  if (modex == 'l')                            /* astro-graph charts */
    j = (i <= THINGS || i > C_HI);
  else if (modex == 'z' || modex == 'g')       /* horizon or zenith charts */
    j = (i < THINGS || i > C_HI);
  else if (modex == 's')                       /* solar system charts */
    j = (i != 2 && (i < THINGS || i > C_HI));
  return j && !ignore[i];                      /* check restriction status */
}


/* Draw the glyph of an object at particular coordinates on the window. */

drawobject(i, x, y)
int i, x, y;
{
  if (!label)    /* If we are inhibiting labels, then do nothing. */
    return;

  /* For other planet centered charts, we have to remember that that     */
  /* particular planet's index now represents the Earth. If we are given */
  /* that index to draw, then change it so we draw the Earth instead.    */

  if (modex != 's' &&
      ((i == centerplanet && i > 2) || (centerplanet == 0 && i == 1)))
    i = 0;
  if (i <= OBJECTS)
    turtle(objectdraw[i], x, y, objectcolor[i]);

  /* Normally we can just go draw the glyph; however, uranians and stars   */
  /* don't have glyphs, so for these draw their three letter abbreviation. */

  else {
    sprintf(string, "%c%c%c", OBJNAM(i));
    drawtext(string, x-strlen(string)*FONTX/2, y+FONTY/2, objectcolor[i]);
  }
}


/* Print a string of text on the graphic window at specified location. To do */
/* this we use Astrolog's own "font" and draw each letter separately.        */

drawtext(string, x, y, o)
char *string;
int x, y;
color o;
{
  int s = scale;
  scale = 100;
  x++;
  y -= FONTY-3;
  while (*string) {
    block(x, y, x+FONTX-1, y+FONTY-2, off);
    turtle(asciidraw[*string-' '], x, y, o);
    x += FONTX;
    string++;
  }
  scale = s;
}


/* Adjust an array of zodiac positions so that no two positions are within   */
/* a certain orb of each other. This is used by the wheel drawing chart      */
/* routines in order to make sure that we don't draw any planet glyphs on    */
/* top of each other. We'll later draw the glyphs at the adjusted positions. */

fillsymbolring(symbol)
double *symbol;
{
  int i, j, k = 1, l, k1, k2;
  double orb = DEFORB*256.0/(double)charty*(double)scale/100.0, temp;

  /* Keep adjusting as long as we can still make changes, or until we do 100 */
  /* rounds. (With many objects, there just may not be enough room for all.) */

  for (l = 0; k && l < 100; l++) {
    k = 0;
    for (i = 1; i <= total; i++) if (proper(i)) {

      /* For each object, determine who is closest on either side. */

      k1 = 1000; k2 = -1000;
      for (j = 1; j <= total; j++)
	if (proper(j) && i != j) {
	  temp = symbol[j]-symbol[i];
	  if (dabs(temp) > 180.0)
	    temp -= DEGREES*sgn(temp);
	  if (temp<k1 && temp>=0.0)
	    k1 = temp;
	  else if (temp>k2 && temp<=0.0)
	    k2 = temp;
	}

      /* If an object's too close on one side, then we move to the other. */

      if (k2>-orb && k1>orb) {
	k = 1; symbol[i] = mod(symbol[i]+orb*0.51+k2*0.49);
      } else if (k1<orb && k2<-orb) {
	k = 1; symbol[i] = mod(symbol[i]-orb*0.51+k1*0.49);

      /* If we are bracketed by close objects on both sides, then let's move */
      /* to the midpoint, so we are as far away as possible from either one. */

      } else if (k2>-orb && k1<orb) {
	k = 1; symbol[i] = mod(symbol[i]+(k1+k2)*0.5);
      }
    }
  }
}


/* Fill in arrays with the sine and cosine values of each degree. This is */
/* used by the wheel chart routines which draw lots of circles.           */

initcircle()
{
  static int done = FALSE, i;
  if (done)                      /* We only need to actually do the calc-   */
    return;                      /* ulations the first time this is called. */
  for (i = 0; i < 360; i++) {
    circx[i] = COSD((double) i);
    circy[i] = SIND((double) i);
  }
  circx[360] = circx[0]; circy[360] = circy[0];
  done = TRUE;
}


/* Get a coordinate based on chart radius, a fraction, and (co)sin value. */
#define POINT(U, R, S) ((int) ((R)*(double)(U)*(S)+0.5))

/* Determine (co)sin factor based on zodiac angle and chart orientation. */
#define PX(A) COSD(180.0-(A)+Asc)
#define PY(A) SIND(180.0-(A)+Asc)

/* Draw a wheel chart, in which the 12 signs and houses are delineated, and  */
/* the planets are inserted in their proper places. This is the default      */
/* graphics chart to generate, as is done when the -v or -w (or no) switches */
/* are included with -X. Draw the aspects in the middle of chart, too.       */

xchartwheel()
{
  int cx = chartx / 2, cy = charty / 2, unit, i, j, count = 0;
  double Asc, orb = DEFORB*256.0/(double)charty*(double)scale/100.0, temp;
  rectall(1, 1, hilite);
  unit = MIN(cx, cy);
  Asc = house[1];
  initcircle();

  /* Draw Ascendant/Descendant and Midheaven/Nadir lines across whole chart. */

  drawline(cx+POINT(unit, 0.99, PX(house[1])),
       cy+POINT(unit, 0.99, PY(house[1])),
       cx+POINT(unit, 0.99, PX(house[7])),
       cy+POINT(unit, 0.99, PY(house[7])), hilite, 1-xcolor);
  drawline(cx+POINT(unit, 0.99, PX(house[10])),
       cy+POINT(unit, 0.99, PY(house[10])),
       cx+POINT(unit, 0.99, PX(house[4])),
       cy+POINT(unit, 0.99, PY(house[4])), hilite, 1-xcolor);

  /* Draw small five or one degree increments around the zodiac sign ring. */

  for (i = 0; i < 360; i += 5-xcolor*4) {
    temp = (double) i;
    drawline(cx+POINT(unit, 0.80, PX(temp)), cy+POINT(unit, 0.80, PY(temp)),
	     cx+POINT(unit, 0.75, PX(temp)), cy+POINT(unit, 0.75, PY(temp)),
	     i%5 ? gray : on, 0);
  }

  /* Draw circles for the zodiac sign and house rings. */

  for (i = 0; i < 360; i++) {
    drawline(cx+POINT(unit, 0.95, circx[i]), cy+POINT(unit, 0.95, circy[i]),
	 cx+POINT(unit, 0.95, circx[i+1]), cy+POINT(unit, 0.95, circy[i+1]),
	 on, 0);
    drawline(cx+POINT(unit, 0.80, circx[i]), cy+POINT(unit, 0.80, circy[i]),
	 cx+POINT(unit, 0.80, circx[i+1]), cy+POINT(unit, 0.80, circy[i+1]),
	 on, 0);
    drawline(cx+POINT(unit, 0.75, circx[i]), cy+POINT(unit, 0.75, circy[i]),
	 cx+POINT(unit, 0.75, circx[i+1]), cy+POINT(unit, 0.75, circy[i+1]),
	 on, 0);
    drawline(cx+POINT(unit, 0.65, circx[i]), cy+POINT(unit, 0.65, circy[i]),
	 cx+POINT(unit, 0.65, circx[i+1]), cy+POINT(unit, 0.65, circy[i+1]),
	 on, 0);
  }

  /* Draw the glyphs for the signs and houses themselves. */

  for (i = 1; i <= SIGNS; i++) {
    temp = (double) (i-1)*30.0;
    drawline(cx+POINT(unit, 0.95, PX(temp)),        /* Draw lines separating */
	 cy+POINT(unit, 0.95, PY(temp)),            /* each sign and house   */
	 cx+POINT(unit, 0.80, PX(temp)),            /* from each other.      */
	 cy+POINT(unit, 0.80, PY(temp)), on, 0);
    drawline(cx+POINT(unit, 0.75, PX(house[i])),
	 cy+POINT(unit, 0.75, PY(house[i])),
	 cx+POINT(unit, 0.65, PX(house[i])),
	 cy+POINT(unit, 0.65, PY(house[i])), on, 0);
    if (xcolor && i%3 != 1)                                    /* Lines from */
      drawline(cx, cy, cx+POINT(unit, 0.65, PX(house[i])),     /* each house */
	       cy+POINT(unit, 0.65, PY(house[i])), gray, 1);   /* to center. */
    temp += 15.0;
    turtle(signdraw[i], cx+POINT(unit, 0.875, PX(temp)),
	   cy+POINT(unit, 0.875, PY(temp)), elemcolor[(i-1)%4]);
    temp = midpoint(house[i], house[mod12(i+1)]);
    turtle(housedraw[i], cx+POINT(unit, 0.70, PX(temp)),
	   cy+POINT(unit, 0.70, PY(temp)), elemcolor[(i-1)%4]);
  }
  for (i = 1; i <= total; i++) {   /* Figure out where to put planet glyphs. */
    symbol[i] = planet[i];
  }
  fillsymbolring(symbol);

  /* For each planet, draw a small dot indicating where it is, and then */
  /* a line from that point to the planet's glyph.                      */

  for (i = 1; i <= total; i++) if (proper(i)) {
    temp = symbol[i];
    drawline(cx+POINT(unit, 0.52, PX(planet[i])),
	     cy+POINT(unit, 0.52, PY(planet[i])),
	     cx+POINT(unit, 0.56, PX(temp)),
	     cy+POINT(unit, 0.56, PY(temp)),
	     ret[i] < 0.0 ? gray : on, (ret[i] < 0.0 ? 1 : 0) - xcolor);
    drawobject(i, cx+POINT(unit, 0.60, PX(temp)),
	       cy+POINT(unit, 0.60, PY(temp)));
    drawpoint(cx+POINT(unit, 0.50, PX(planet[i])),
	      cy+POINT(unit, 0.50, PY(planet[i])), objectcolor[i]);
  }
  if (bonus)    /* Don't draw aspects in bonus mode. */
    return;

  /* Draw lines connecting planets which have aspects between them. */

  creategrid(FALSE);
  for (j = total; j >= 2; j--)
    for (i = j-1; i >= 1; i--)
      if (gridname[i][j] && proper(i) && proper(j))
	drawline(cx+POINT(unit, 0.48, PX(planet[i])),
		 cy+POINT(unit, 0.48, PY(planet[i])),
		 cx+POINT(unit, 0.48, PX(planet[j])),
		 cy+POINT(unit, 0.48, PY(planet[j])),
		 aspectcolor[gridname[i][j]], abs(grid[i][j]/60/2));
}


/* Draw another wheel chart; however, this time we have two rings of planets */
/* because we are doing a relationship chart between two sets of data. This  */
/* chart is obtained when the -r0 is combined with the -X switch.            */

xchartwheelrelation()
{
  int cx = chartx / 2, cy = charty / 2, unit, i, j;
  double Asc, temp;
  rectall(1, 1, hilite);
  unit = MIN(cx, cy);
  Asc = house1[1];
  initcircle();

  /* Draw the horizon and meridian lines across whole chart, and draw the */
  /* zodiac and house rings, exactly like before. We are drawing only the */
  /* houses of one of the two charts in the relationship, however.        */

  drawline(cx+POINT(unit, 0.99, PX(house1[1])),
       cy+POINT(unit, 0.99, PY(house1[1])),
       cx+POINT(unit, 0.99, PX(house1[7])),
       cy+POINT(unit, 0.99, PY(house1[7])), hilite, 1-xcolor);
  drawline(cx+POINT(unit, 0.99, PX(house1[10])),
       cy+POINT(unit, 0.99, PY(house1[10])),
       cx+POINT(unit, 0.99, PX(house1[4])),
       cy+POINT(unit, 0.99, PY(house1[4])), hilite, 1-xcolor);
  for (i = 0; i < 360; i += 5-xcolor*4) {
    temp = (double) i;
    drawline(cx+POINT(unit, 0.82, PX(temp)), cy+POINT(unit, 0.82, PY(temp)),
	     cx+POINT(unit, 0.78, PX(temp)), cy+POINT(unit, 0.78, PY(temp)),
	     i%5 ? gray : on, 0);
  }
  for (i = 0; i < 360; i++) {
    drawline(cx+POINT(unit, 0.95, circx[i]), cy+POINT(unit, 0.95, circy[i]),
	 cx+POINT(unit, 0.95, circx[i+1]), cy+POINT(unit, 0.95, circy[i+1]),
	 on, 0);
    drawline(cx+POINT(unit, 0.82, circx[i]), cy+POINT(unit, 0.82, circy[i]),
	 cx+POINT(unit, 0.82, circx[i+1]), cy+POINT(unit, 0.82, circy[i+1]),
	 on, 0);
    drawline(cx+POINT(unit, 0.78, circx[i]), cy+POINT(unit, 0.78, circy[i]),
	 cx+POINT(unit, 0.78, circx[i+1]), cy+POINT(unit, 0.78, circy[i+1]),
	 on, 0);
    drawline(cx+POINT(unit, 0.70, circx[i]), cy+POINT(unit, 0.70, circy[i]),
	 cx+POINT(unit, 0.70, circx[i+1]), cy+POINT(unit, 0.70, circy[i+1]),
	 on, 0);
  }
  for (i = 1; i <= SIGNS; i++) {
    temp = (double) (i-1)*30.0;
    drawline(cx+POINT(unit, 0.95, PX(temp)),
	 cy+POINT(unit, 0.95, PY(temp)),
	 cx+POINT(unit, 0.82, PX(temp)),
	 cy+POINT(unit, 0.82, PY(temp)), on, 0);
    drawline(cx+POINT(unit, 0.78, PX(house1[i])),
	 cy+POINT(unit, 0.78, PY(house1[i])),
	 cx+POINT(unit, 0.70, PX(house1[i])),
	 cy+POINT(unit, 0.70, PY(house1[i])), on, 0);
    if (xcolor && i%3 != 1)
      drawline(cx, cy, cx+POINT(unit, 0.70, PX(house1[i])),
	       cy+POINT(unit, 0.70, PY(house1[i])), gray, 1);
    temp += 15.0;
    turtle(signdraw[i], cx+POINT(unit, 0.885, PX(temp)),
	   cy+POINT(unit, 0.885, PY(temp)), elemcolor[(i-1)%4]);
    temp = midpoint(house1[i], house1[mod12(i+1)]);
    turtle(housedraw[i], cx+POINT(unit, 0.74, PX(temp)),
	   cy+POINT(unit, 0.74, PY(temp)), elemcolor[(i-1)%4]);
  }

  /* Draw the outer ring of planets (based on the planets in the chart     */
  /* which the houses do not reflect - the houses belong to the inner ring */
  /* below). Draw each glyph, a line from it to its actual position point  */
  /* in the outer ring, and then draw another line from this point to a    */
  /* another dot at the same position in the inner ring as well.           */

  for (i = 1; i <= total; i++) {
    symbol[i] = planet2[i];
  }
  fillsymbolring(symbol);
  for (i = 1; i <= total; i++) if (proper(i)) {
    temp = symbol[i];
    drawline(cx+POINT(unit, 0.58, PX(planet2[i])),
	     cy+POINT(unit, 0.58, PY(planet2[i])),
	     cx+POINT(unit, 0.61, PX(temp)),
	     cy+POINT(unit, 0.61, PY(temp)),
	     ret2[i] < 0.0 ? gray : on, (ret2[i] < 0.0 ? 1 : 0) - xcolor);
    drawobject(i, cx+POINT(unit, 0.65, PX(temp)),
	       cy+POINT(unit, 0.65, PY(temp)));
    drawpoint(cx+POINT(unit, 0.56, PX(planet2[i])),
	      cy+POINT(unit, 0.56, PY(planet2[i])), objectcolor[i]);
    drawpoint(cx+POINT(unit, 0.43, PX(planet2[i])),
	      cy+POINT(unit, 0.43, PY(planet2[i])), objectcolor[i]);
    drawline(cx+POINT(unit, 0.45, PX(planet2[i])),
	     cy+POINT(unit, 0.45, PY(planet2[i])),
	     cx+POINT(unit, 0.54, PX(planet2[i])),
	     cy+POINT(unit, 0.54, PY(planet2[i])),
	     ret2[i] < 0.0 ? gray : on, 2-xcolor);
  }

  /* Now draw the inner ring of planets. If it weren't for the outer ring,  */
  /* this would be just like the standard non-relationship wheel chart with */
  /* only one set of planets. Again, draw glyph, and a line to true point.  */

  for (i = 1; i <= total; i++) {
    symbol[i] = planet1[i];
  }
  fillsymbolring(symbol);
  for (i = 1; i <= total; i++) if (proper(i)) {
    temp = symbol[i];
    drawline(cx+POINT(unit, 0.45, PX(planet1[i])),
	     cy+POINT(unit, 0.45, PY(planet1[i])),
	     cx+POINT(unit, 0.48, PX(temp)),
	     cy+POINT(unit, 0.48, PY(temp)),
	     ret1[i] < 0.0 ? gray : on, (ret1[i] < 0.0 ? 1 : 0) - xcolor);
    drawobject(i, cx+POINT(unit, 0.52, PX(temp)),
	       cy+POINT(unit, 0.52, PY(temp)));
    drawpoint(cx+POINT(unit, 0.43, PX(planet1[i])),
	      cy+POINT(unit, 0.43, PY(planet1[i])), objectcolor[i]);
  }
  if (bonus)    /* Don't draw aspects in bonus mode. */
    return;

  /* Draw lines connecting planets between the two charts that have aspects. */

  createrelationgrid(FALSE);
  for (j = total; j >= 1; j--)
    for (i = total; i >= 1; i--)
      if (gridname[i][j] && proper(i) && proper(j))
	drawline(cx+POINT(unit, 0.41, PX(planet1[j])),
		 cy+POINT(unit, 0.41, PY(planet1[j])),
		 cx+POINT(unit, 0.41, PX(planet2[i])),
		 cy+POINT(unit, 0.41, PY(planet2[i])),
		 aspectcolor[gridname[i][j]], abs(grid[i][j]/60/2));
}


/* Given longitude and latitude values on a globe, return the window        */
/* coordinates corresponding to them. In other words, project the globe     */
/* onto the view plane, and return where our coordinates got projected to,  */
/* as well as whether our location is hidden on the back side of the globe. */

int globecalc(x1, y1, u, v, cx, cy, rx, ry, deg)
double x1, y1;
int *u, *v, cx, cy, rx, ry, deg;
{
  double j;

  /* Compute coordinates for a general globe invoked with -XG switch. */

  if (modex == 'g') {
    x1 = mod(x1+(double)deg);    /* Shift by current globe rotation value. */
    if (tilt != 0.0) {
      x1 = DTOR(x1); y1 = DTOR(90.0-y1);         /* Do another coordinate  */
      coorxform(&x1, &y1, tilt / DEGTORAD);      /* shift if the globe's   */
      x1 = mod(RTOD(x1)); y1 = 90.0-RTOD(y1);    /* equator is tilted any. */
    }
    *v = cy + (int) ((double)(ry+1)*-COSD(y1));
    *u = cx + (int) ((double)(rx+1)*-COSD(x1)*SIND(y1));
    return x1 > 180.0;
  }

  /* Compute coordinates for a polar globe invoked with -XP switch. */

  j = bonus ? 90.0+x1+deg : 270.0-x1-deg;
  *v = cy + (int) (SIND(y1)*(double)(ry+1)*SIND(j));
  *u = cx + (int) (SIND(y1)*(double)(rx+1)*COSD(j));
  return bonus ? y1 < 90.0 : y1 > 90.0;
}


/* Draw a globe in the window, based on the specified rotational and tilt  */
/* values. In addition, we may draw in each planet at its zenith position. */

drawglobe(deg)
int deg;
{
  char *nam, *loc, *lin, d;
  int X[TOTAL+1], Y[TOTAL+1], M[TOTAL+1], N[TOTAL+1],
    cx = chartx / 2, cy = charty / 2, rx, ry, lon, lat, unit = 12*scale/100,
    x, y, m, n, u, v, i, J, k, l;
  double planet1[TOTAL+1], planet2[TOTAL+1], x1, y1, j;
  color c;
  rx = cx-1; ry = cy-1;

  /* Loop through each coastline string, drawing visible parts on the globe. */

  while (readworlddata(&nam, &loc, &lin)) {
    i = nam[0]-'0';
    c = (modex == 'g' && bonus) ? gray : (i ? rainbowcolor[i] : maincolor[6]);

    /* Get starting longitude and latitude of current coastline piece. */

    lon = (loc[0] == '+' ? 1 : -1)*
      ((loc[1]-'0')*100 + (loc[2]-'0')*10 + (loc[3]-'0'));
    lat = (loc[4] == '+' ? 1 : -1)*((loc[5]-'0')*10 + (loc[6]-'0'));
    x = 180-lon;
    y = 90-lat;
    globecalc((double) x, (double) y, &m, &n, cx, cy, rx, ry, deg);

    /* Go down the coastline piece, drawing each visible segment on globe. */

    for (i = 0; d = lin[i]; i++) {
      if (d == 'L' || d == 'H' || d == 'G')
	x--;
      else if (d == 'R' || d == 'E' || d == 'F')
	x++;
      if (d == 'U' || d == 'H' || d == 'E')
	y--;
      else if (d == 'D' || d == 'G' || d == 'F')
	y++;
      if (x > 359)
	x = 0;
      else if (x < 0)
	x = 359;
      if (!globecalc((double) x, (double) y, &u, &v, cx, cy, rx, ry, deg))
	drawline(m, n, u, v, c, 0);
      m = u; n = v;
    }
  }

  /* Finally, draw the circular outline of the globe itself. */

  m = cx+rx+1; n = cy;
  for (i = 0; i <= 360; i++) {
    u = cx+(rx+1)*COSD((double)i); v = cy+(ry+1)*SIND((double)i);
    u = MIN(u, cx+rx); v = MIN(v, cy+ry);
    drawline(m, n, u, v, on, 0); m = u; n = v;
  }

  /* Now, only if we are in bonus chart mode, draw each planet at its */
  /* zenith location on the globe, assuming that location is visible. */

  if (modex != 'g' || !bonus)
    return;
  j = Lon;
  if (j < 0.0)
    j += DEGREES;
  for (i = 1; i <= total; i++) {
    planet1[i] = DTOR(planet[i]);
    planet2[i] = DTOR(planetalt[i]);
    ecltoequ(&planet1[i], &planet2[i]);    /* Calculate zenith long. & lat. */
  }
  for (i = 1; i <= total; i++) if (proper(i)) {
    x1 = planet1[18]-planet1[i];
    if (x1 < 0.0)
      x1 += 2.0*PI;
    if (x1 > PI)
      x1 -= 2.0*PI;
    x1 = mod(180.0-j-RTOD(x1));
    y1 = 90.0-RTOD(planet2[i]);
    X[i] = globecalc(x1, y1, &u, &v, cx, cy, rx, ry, deg) ? -1000 : u;
    Y[i] = v; M[i] = X[i]; N[i] = Y[i]+unit/2;
  }

  /* Now that we have the coordinates of each object, figure out where to    */
  /* draw the glyphs. Again, we try not to draw glyphs on top of each other. */

  for (i = 1; i <= total; i++) if (proper(i)) {
    k = l = chartx+charty;

    /* For each planet, we draw the glyph either right over or right under */
    /* the actual zenith location point. So, find out the closest distance */
    /* of any other planet assuming we place ours at both possibilities.   */

    for (J = 1; J < i; J++) if (proper(J)) {
      k = MIN(k, abs(M[i]-M[J])+abs(N[i]-N[J]));
      l = MIN(l, abs(M[i]-M[J])+abs(N[i]-unit-N[J]));
    }

    /* Normally, we put the glyph right below the actual point. If however   */
    /* another planet is close enough to have their glyphs overlap, and the  */
    /* above location is better of, then we'll draw the glyph above instead. */

    if (k < unit || l < unit)
      if (k < l)
	N[i] -= unit;
  }
  for (i = total; i >= 1; i--) if (X[i] >= 0 && proper(i))      /* Draw the */
    drawobject(i, M[i], N[i]);                                  /* glyphs.  */
  for (i = total; i >= 1; i--) if (X[i] >= 0 && proper(i)) {
#ifdef X11
    if (!xbitmap)
      Xcolor(objectcolor[i]);
#endif
    drawspot(X[i], Y[i], objectcolor[i]);
  }
}


/* Draw one "Ley line" on the world map, based coordinates given in terms of */
/* longitude and vertical fractional distance from the center of the earth.  */

drawleyline(l1, f1, l2, f2, o)
double l1, f1, l2, f2;
bit o;
{
  l1 = mod(l1); l2 = mod(l2);

  /* Convert vertical fractional distance to a corresponding coordinate. */

  f1 = 90.0-ASIN(f1)/(PI/2.0)*90.0; f2 = 90.0-ASIN(f2)/(PI/2.0)*90.0;
  drawline2((int) (l1*(double)(scale/100)+0.5)+1,
	    (int) (f1*(double)(scale/100)+0.5)+1,
	    (int) (l2*(double)(scale/100)+0.5)+1,
	    (int) (f2*(double)(scale/100)+0.5)+1, o);
}


/* Draw the main set of planetary Ley lines on the map of the world. This */
/* consists of drawing an icosahedron and then a dodecahedron lattice.    */

drawleylines(deg)
int deg;
{
  color icosa, dodeca;
  double off = (double) deg, phi, h, h1, h2, r, i;
  icosa = aspectcolor[5]; dodeca = hilite;
  phi = (sqrt(5.0)+1.0)/2.0;                     /* Icosahedron constants. */
  h = 1.0/(phi*2.0-1.0);
  for (i = off; i < DEGREES+off; i += 72.0) {    /* Draw icosahedron edges. */
    drawleyline(i, h, i+72.0, h, icosa);
    drawleyline(i-36.0, -h, i+36.0, -h, icosa);
    drawleyline(i, h, i, 1.0, icosa);
    drawleyline(i+36.0, -h, i+36.0, -1.0, icosa);
    drawleyline(i, h, i+36.0, -h, icosa);
    drawleyline(i, h, i-36.0, -h, icosa);
  }
  r = 1.0/sqrt(3.0)/phi/cos(DTOR(54.0));         /* Dodecahedron constants. */
  h2 = sqrt(1.0-r*r); h1 = h2/(phi*2.0+1.0);
  for (i = off; i < DEGREES+off; i += 72.0) {    /* Draw docecahedron edges. */
    drawleyline(i-36.0, h2, i+36.0, h2, dodeca);
    drawleyline(i, -h2, i+72.0, -h2, dodeca);
    drawleyline(i+36.0, h2, i+36.0, h1, dodeca);
    drawleyline(i, -h2, i, -h1, dodeca);
    drawleyline(i+36.0, h1, i+72.0, -h1, dodeca);
    drawleyline(i+36.0, h1, i, -h1, dodeca);
  }
}


/* Draw a map of the world on the screen. This is similar to drawing the  */
/* globe, but is simplified because this is just a rectangular image, and */
/* the window coordinates are proportional to the longitude and latitude. */

drawworld(deg)
int deg;
{
  char *nam, *loc, *lin, d;
  int lon, lat, x, y, xold, yold, i;
  color c;

  /* This shouldn't happen unless someone changes the max window size. */

  if (chartx > BITMAPX) {
    fprintf(stderr, "Scale factor of %d too large for worldmap.\n", scale);
    exit(1);
  }

  /* Loop through each coastline string, drawing it on the world map. */

  while (readworlddata(&nam, &loc, &lin)) {
    i = nam[0]-'0';
    c = modex == 'l' ? on : (i ? rainbowcolor[i] : maincolor[6]);

    /* Get starting longitude and latitude of current coastline piece. */

    lon = (loc[0] == '+' ? 1 : -1)*
      ((loc[1]-'0')*100 + (loc[2]-'0')*10 + (loc[3]-'0'));
    lat = (loc[4] == '+' ? 1 : -1)*((loc[5]-'0')*10 + (loc[6]-'0'));
    xold = x = (int) mod((double)(180-lon+deg));
    yold = y = 91-lat;

    /* Go down the coastline piece, drawing each segment on world map. */

    for (i = 0; d = lin[i]; i++) {
      if (d == 'L' || d == 'H' || d == 'G')
	x--;
      else if (d == 'R' || d == 'E' || d == 'F')
	x++;
      if (d == 'U' || d == 'H' || d == 'E')
	y--;
      else if (d == 'D' || d == 'G' || d == 'F')
	y++;
      if (x > 360) {
	x = 1;
	xold = 0;
      }

      /* If we are doing a Mollewide map projection, then transform the */
      /* coordinates appropriately before drawing the segment.          */

      if ((todisplay & DASHXW0) > 0 && modex != 'l')
	drawline((180+(xold-180)*(int)sqrt((double)(32400-4*(yold-91)*(yold-91)
                 ))/180)*scale/100, yold*scale/100,
		 (180+(x-180)*(int)sqrt((double)(32400-4*(y-91)*(y-91)))/180)
                 *scale/100, y*scale/100, c, 0);
      else
	drawline(xold*scale/100, yold*scale/100,
		 x*scale/100, y*scale/100, c, 0);
      if (x < 1)
	x = 360;
      xold = x; yold = y;
    }
  }

  /* Again, if we are doing the non-rectangular Mollewide map projection, */
  /* draw the outline of the globe/map itself.                            */

  if ((todisplay & DASHXW0) > 0 && modex != 'l') {
    if (!bonus)
      for (xold = 0, y = -89; y <= 90; y++, xold = x)
	for (x = (int)(sqrt((double)(32400-4*y*y))+0.5), i = -1; i < 2; i += 2)
	  drawline((180+i*xold)*scale/100, (90+y)*scale/100,
		   (180+i*x)*scale/100, (91+y)*scale/100, on, 0);
  } else
    rectall(1, 1, hilite);
}


/* Draw a normal line on the screen; however, if the x coordinates are close */
/* to opposite sides of the window, then we assume that the line runs off    */
/* one side and reappears on the other, so draw the appropriate two lines    */
/* instead. This is used by the Ley line and astro-graph routines, which     */
/* draw lines running around the world and hence off the edges of the maps.  */

drawline2(xold, yold, xnew, ynew, o)
int xold, yold, xnew, ynew;
bit o;
{
  int xmid, ymid, i;
  if (xold < 0) {
    drawpoint(xnew, ynew, o);
    return;
  }
  xmid = 180*scale/100;

  /* If endpoints aren't near opposite edges, just draw the line and return. */

  if (dabs((double)(xnew-xold)) < (double) xmid) {
    drawline(xold, yold, xnew, ynew, o, 0);
    return;
  }
  i = xold < xmid ? xold+chartx-xnew-2 : xnew+chartx-xold-2;

  /* Determine vertical coordinate where our line runs off edges of screen. */

  ymid = yold+(int)((double)(ynew-yold)*
    (xold < xmid ? (double)(xold-1) : (double)(chartx-xold-2))/(double)i);
  drawline(xold, yold, xold < xmid ? 1 : chartx-2, ymid, o, 0);
  drawline(xnew < xmid ? 1 : chartx-2, ymid, xnew, ynew, o, 0);
}


/* Adjust an array of longitude positions so that no two are within a    */
/* certain orb of each other. This is used by the astro-graph routine to */
/* make sure we don't draw any planet glyphs marking the lines on top of */
/* each other. This is almost identical to the fillsymbolring() routine  */
/* used by the wheel charts; however, there the glyphs are placed in a   */
/* continuous ring, while here we have the left and right screen edges.  */
/* Also, here we are placing two sets of planets at the same time.       */

fillsymbolline(symbol)
double *symbol;
{
  double orb = DEFORB*1.35*(double)scale/100.0,
    max = DEGREES*(double)scale/100.0, temp;
  int i, j, k = 1, l, k1, k2;

  /* Keep adjusting as long as we can still make changes. */

  for (l = 0; k && l < 100; l++) {
    k = 0;
    for (i = 1; i <= TOTAL*2; i++)
      if (proper((i+1)/2) && symbol[i] >= 0.0) {

	/* For each object, determine who is closest to the left and right. */

	k1 = max-symbol[i]; k2 = -symbol[i];
	for (j = 1; j <= THINGS*2; j++) {
	  if (proper((j+1)/2) && i != j) {
	    temp = symbol[j]-symbol[i];
	    if (temp<k1 && temp>=0.0)
	      k1 = temp;
	    else if (temp>k2 && temp<=0.0)
	      k2 = temp;
	  }
	}

	/* If an object's too close on one side, then we move to the other. */

	if (k2>-orb && k1>orb) {
	  k = 1; symbol[i] = symbol[i]+orb*0.51+k2*0.49;
	} else if (k1<orb && k2<-orb) {
	  k = 1; symbol[i] = symbol[i]-orb*0.51+k1*0.49;
	} else if (k2>-orb && k1<orb) {
	  k = 1; symbol[i] = symbol[i]+(k1+k2)*0.5;
	}
      }
  }
}


/* Draw an astro-graph chart on a map of the world, i.e. the draw the     */
/* Ascendant, Descendant, Midheaven, and Nadir lines corresponding to the */
/* time in the chart. This chart is done when the -L switch is combined   */
/* with the -X switch.                                                    */

xchartastrograph()
{
  double planet1[TOTAL+1], planet2[TOTAL+1],
    end1[TOTAL*2+1], end2[TOTAL*2+1],
    symbol1[TOTAL*2+1], symbol2[TOTAL*2+1],
    lon = Lon, longm, x, y, z, ad, oa, am, od, dm, lat;
  int unit, lat1 = -60, lat2 = 75, y1, y2, xold1, xold2, i, j, k;
  unit = scale/100;

  /* Erase top and bottom parts of map. We don't draw the astro-graph lines */
  /* above certain latitudes, and this gives us room for glyph labels, too. */

  y1 = (91-lat1)*scale/100.0;
  y2 = (91-lat2)*scale/100.0;
  block(1, 1, chartx-2, y2-1, off);
  block(1, charty-2, chartx-2, y1+1, off);
  drawline(0, charty/2, chartx-2, charty/2, hilite, 4);    /* Draw equator. */
  drawline(1, y2, chartx-2, y2, on, 0);
  drawline(1, y1, chartx-2, y1, on, 0);
  for (i = 1; i <= total*2; i++)
    end1[i] = end2[i] = -1000.0;

  /* Draw small hatches every 5 degrees along edges of world map. */

  for (i = lat1; i <= lat2; i += 5) {
    j = (91-i)*scale/100.0;
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0);
    drawline(1, j, k, j, hilite, 0);
    drawline(chartx-2, j, chartx-1-k, j, hilite, 0);
  }
  for (i = -180; i < 180; i += 5) {
    j = (180-i)*scale/100.0;
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0)+(i/90*90 == i ? 1 : 0);
    drawline(j, y2+1, j, y2+k, hilite, 0);
    drawline(j, y1-1, j, y1-k, hilite, 0);
  }

  /* Calculate zenith locations of each planet. */

  for (i = 1; i <= total; i++) {
    planet1[i] = DTOR(planet[i]);
    planet2[i] = DTOR(planetalt[i]);
    ecltoequ(&planet1[i], &planet2[i]);
  }

  /* Draw the Midheaven lines and zenith location markings. */

  if (lon < 0.0)
    lon += DEGREES;
  for (i = 1; i <= total; i++) if (proper(i)) {
    x = DTOR(MC)-planet1[i];
    if (x < 0.0)
      x += 2.0*PI;
    if (x > PI)
      x -= 2.0*PI;
    z = lon+RTOD(x);
    if (z > 180.0)
      z -= DEGREES;
    j = (int) (mod(180.0-z+degree)*(double)scale/100.0);
    drawline(j, y1+unit*4, j, y2-unit*1, elemcolor[1], 0);
    end2[i*2-1] = (double) j;
    y = RTOD(planet2[i]);
    k = (int) ((91.0-y)*(double)scale/100.0);
    block(j-1, k-1, j+1, k+1, hilite);
    block(j, k, j, k, off);

    /* Draw Nadir lines assuming we aren't in bonus chart mode. */

    if (!bonus) {
      j += 180*scale/100;
      if (j > chartx-2)
	j -= (chartx-2);
      end1[i*2-1] = (double) j;
      drawline(j, y1+unit*2, j, y2-unit*2, elemcolor[3], 0);
    }
  }

  /* Now, normally, unless we are in bonus chart mode, we will go on to draw */
  /* the Ascendant and Descendant lines here.                                */

  longm = DTOR(mod(MC+lon));
  if (!bonus) for (i = 1; i <= total; i++) if (proper(i)) {
    xold1 = xold2 = -1000;
    for (lat = (double) lat1; lat <= (double) lat2;
	 lat += 100.0/(double)scale) {

      /* First compute and draw the current segment of Ascendant line. */

      j = (int) ((91.0-lat)*(double)scale/100.0);
      ad = tan(planet2[i])*tan(DTOR(lat));
      if (ad*ad > 1.0)
	ad = 1000.0;
      else {
	ad = ASIN(ad);
	oa = planet1[i]-ad;
	if (oa < 0.0)
	  oa += 2.0*PI;
	am = oa-PI/2.0;
	if (am < 0.0)
	  am += 2.0*PI;
	z = longm-am;
	if (z < 0.0)
	  z += 2.0*PI;
	if (z > PI)
	  z -= 2.0*PI;
	z = RTOD(z);
	k = (int) (mod(180.0-z+degree)*(double)scale/100.0);
	drawline2(xold1, j+1, k, j, elemcolor[0]);
	if (lat == (double) lat1) {                          /* Line segment */
	  drawline(k, y1, k, y1+unit*4, elemcolor[0], 0);    /* pointing to  */
	  end2[i*2] = (double) k;                            /* Ascendant.   */
	}
	xold1 = k;
      }

      /* The curving Ascendant and Descendant lines actually touch each at  */
      /* low latitudes. Sometimes when we start out, a particular planet's  */
      /* lines haven't appeared yet, i.e. we are scanning at a latitude     */
      /* where our planet's lines don't exist. If this is the case, then    */
      /* when they finally do start, draw a thin horizontal line connecting */
      /* the Ascendant and Descendant lines so they don't just start in     */
      /* space. Note that these connected lines aren't labeled with glyphs. */

      if (ad == 1000.0) {
	if (xold1 >= 0) {
	  drawline2(xold1, j+1, xold2, j+1, gray);
	  lat = 90.0;
	}
      } else {

      /* Then compute and draw corresponding segment of Descendant line. */

	od = planet1[i]+ad;
	dm = od+PI/2.0;
	z = longm-dm;
	if (z < 0.0)
	  z += 2.0*PI;
	if (z > PI)
	  z -= 2.0*PI;
	z = RTOD(z);
	k = (int) (mod(180.0-z+degree)*(double)scale/100.0);
	drawline2(xold2, j+1, k, j, elemcolor[2]);
	if (xold2 < 0)
	  drawline2(xold1, j, k, j, gray);
	if (lat == (double) lat1)                            /* Line segment */
	  drawline(k, y1, k, y1+unit*2, elemcolor[2], 0);    /* pointing to  */
	xold2 = k;                                           /* Descendant.  */
      }
    }

    /* Draw segments pointing to top of Ascendant and Descendant lines. */

    if (ad != 1000.0) {
      drawline(xold1, y2, xold1, y2-unit*1, elemcolor[0], 0);
      drawline(k, y2, k, y2-unit*2, elemcolor[2], 0);
      end1[i*2] = (double) k;
    }
  }
#ifdef X11
  if (!xbitmap)
    Xcolor(on);
#endif

  /* Determine where to draw the planet glyphs. We have four sets of each    */
  /* planet - each planet's glyph appearing in the chart up to four times -  */
  /* one for each type of line. The Midheaven and Ascendant lines are always */
  /* labeled at the bottom of the chart, while the Nadir and Midheaven lines */
  /* at the top. Therefore we need to place two sets of glyphs, twice.       */

  for (i = 1; i <= total*2; i++) {
    symbol1[i] = end1[i];
    symbol2[i] = end2[i];
  }
  fillsymbolline(symbol1);
  fillsymbolline(symbol2);

  /* Now actually draw the planet glyphs. */

  for (i = 1; i <= total*2; i++) {
    j = (i+1)/2;
    if (proper(j)) {
      if ((turtlex = (int) symbol1[i]) > 0) {
	drawline((int) end1[i], y2-unit*2, (int) symbol1[i], y2-unit*4,
		 ret[j] ? gray : on, (ret[i] < 0.0 ? 1 : 0) - xcolor);
	drawobject(j, turtlex, y2-unit*10);
      }
      if ((turtlex = (int) symbol2[i]) > 0) {
	drawline((int) end2[i], y1+unit*4, (int) symbol2[i], y1+unit*8,
		 ret[j] ? gray : on, (ret[i] < 0.0 ? 1 : 0) - xcolor);
	drawobject(j, turtlex, y1+unit*14);
	turtle(objectdraw[i & 1 ? 18 : 19], (int) symbol2[i],
	       y1+unit*24, objectcolor[j]);
      }
    }
  }
}


/* Draw an aspect and midpoint grid in the window, with planets labeled down */
/* the diagonal. This chart is done when the -g switch is combined with the  */
/* -X switch. The chart is always 20 by 20 cells in size; therefore based on */
/* how the restrictions are set up, there may be blank columns and rows, or  */
/* else only the first 20 unrestricted objects will be included.             */

xchartaspectgrid()
{
  int unit, siz, x, y, i, j, k;
  color c;
  if (chartx > BITMAPX) {
    fprintf(stderr, "Scale factor of %d too large for aspect grid.\n", scale);
    exit(1);
  }
  unit = CELLSIZE*scale/100; siz = OBJECTS*unit;
  creategrid(bonus);
  
  /* Loop through each cell in each row and column of grid. */

  for (y = 1, j = 0; y <= OBJECTS; y++) {
    do {
      j++;
    } while (ignore[j] && j <= total);
    drawline(0, y*unit, siz, y*unit, gray, 1-xcolor);
    drawline(y*unit, 0, y*unit, siz, gray, 1-xcolor);
    if (j <= total) for (x = 1, i = 0; x <= OBJECTS; x++) {
      do {
	i++;
      } while (ignore[i] && i <= total);
      if (i <= total) {
	turtlex = x*unit-unit/2;
	turtley = y*unit-unit/2 - (scale > 200 ? 5 : 0);

	/* If this is an aspect cell, draw glyph of aspect in effect. */

	if (bonus ? x > y : x < y)
	  turtle(aspectdraw[gridname[i][j]], turtlex,
		 turtley, c = aspectcolor[gridname[i][j]]);

	/* If this is a midpoint cell, draw glyph of sign of midpoint. */

	else if (bonus ? x < y : x > y)
	  turtle(signdraw[gridname[i][j]], turtlex,
		 turtley, c = elemcolor[(gridname[i][j]-1)%4]);

	/* For cells on main diagonal, draw glyph of planet. */

	else {
	  edge((y-1)*unit, (y-1)*unit, y*unit, y*unit, hilite);
	  drawobject(i, turtlex, turtley);
	}

	/* When the scale size is 300, we can print text in each cell: */

	if (!xbitmap && scale > 200 && label) {
	  k = abs(grid[i][j]);

	  /* For the aspect portion, print the orb in degrees and minutes. */

	  if (bonus ? x > y : x < y) {
	    if (gridname[i][j])
	      sprintf(string, "%c%d %d%d'", k != grid[i][j] ? '-' : '+',
		      k/60, (k%60)/10, (k%60)%10);
	    else
	      sprintf(string, "");

	  /* For the midpoint portion, print the degrees and minutes. */

	  } else if (bonus ? x < y : x > y)
	    sprintf(string, "%d%d %d%d'",
		    (k/60)/10, (k/60)%10, (k%60)/10, (k%60)%10);

	  /* For the main diagonal, print degree and sign of each planet. */

	  else {
	    c = elemcolor[(gridname[i][j]-1)%4];
	    sprintf(string, "%c%c%c %d%d", SIGNAM(gridname[i][j]), k/10, k%10);
	  }
	  drawtext(string, x*unit-unit/2-FONTX*3, y*unit-3, c);
	}
      }
    }
  }
  rectall(1, 1, hilite);
}


/* Draw an aspect (or midpoint) grid in the window, between the planets in  */
/* two different charts, with the planets labeled at the top and side. This */
/* chart is done when the -g switch is combined with the -r0 and -X switch. */
/* Like above, the chart always has a fixed number of cells.                */

xchartrelationaspectgrid()
{
  int unit, siz, x, y, i, j, k, l;
  color c;
  if (chartx > BITMAPX) {
    fprintf(stderr, "Scale factor of %d too large for aspect grid.\n", scale);
    exit(1);
  }
  unit = CELLSIZE*scale/100; siz = (OBJECTS+1)*unit;
  createrelationgrid(bonus);
  for (y = 0, j = -1; y <= OBJECTS; y++) {
    do {
      j++;
    } while (ignore[j] && j <= total);
    drawline(0, (y+1)*unit, siz, (y+1)*unit, gray, 1-xcolor);
    drawline((y+1)*unit, 0, (y+1)*unit, siz, gray, 1-xcolor);
    edge(0, y*unit, unit, (y+1)*unit, hilite);
    edge(y*unit, 0, (y+1)*unit, unit, hilite);
    if (j <= total) for (x = 0, i = -1; x <= OBJECTS; x++) {
      do {
	i++;
      } while (ignore[i] && i <= total);

      /* Again, we are looping through each cell in each row and column. */

      if (i <= total) {
	turtlex = x*unit+unit/2;
	turtley = y*unit+unit/2 - (!xbitmap && scale > 200 ? 5 : 0);

	/* If current cell is on top row or left hand column, draw glyph */
	/* of planet owning the particular row or column in question.    */

	if (y == 0 || x == 0) {
	  if (x+y > 0)
	    drawobject(j == 0 ? i : j, turtlex, turtley);
	} else {

	/* Otherwise, draw glyph of aspect in effect, or glyph of */
	/* sign of midpoint, between the two planets in question. */

	  if (!bonus)
	    turtle(aspectdraw[gridname[i][j]], turtlex, turtley,
		   c = aspectcolor[gridname[i][j]]);
	  else
	    turtle(signdraw[gridname[i][j]], turtlex, turtley,
		   c = elemcolor[(gridname[i][j]-1)%4]);
	}

	/* Again, when scale size is 300, print some text in current cell: */

	if (!xbitmap && scale > 200 && label) {

	  /* For top and left edges, print sign and degree of the planet. */

	  if (y == 0 || x == 0) {
	    if (x+y > 0) {
	      k = (int)((y == 0 ? planet2[i] : planet1[j])/30.0)+1;
	      l = (int)((y == 0 ? planet2[i] : planet1[j])-(double)(k-1)*30.0);
	      c = elemcolor[(k-1)%4];
	      sprintf(string, "%c%c%c %d%d", SIGNAM(k), l/10, l%10);

	      /* For extreme upper left corner, print some little arrows */
	      /* pointing out chart1's planets and chart2's planets.     */

	    } else {
	      c = hilite;
	      sprintf(string, "1v 2->");
	    }
	  } else {
	    k = abs(grid[i][j]);

	    /* For aspect cells, print the orb in degrees and minutes. */

	    if (!bonus)
	      if (gridname[i][j])
		sprintf(string, "%c%d %d%d'", k != grid[i][j] ? (todisplay &
		        DASHGA ? 'a' : '-') : (todisplay & DASHGA ? 's' : '+'),
			k/60, (k%60)/10, (k%60)%10);
	      else
		sprintf(string, "");

	    /* For midpoint cells, print degree and minute. */

	    else
	      sprintf(string, "%d%d %d%d'",
		      (k/60)/10, (k/60)%10, (k%60)/10, (k%60)%10);
	  }
	  drawtext(string, x*unit+unit/2-FONTX*3, (y+1)*unit-3, c);
	}
      }
    }
  }
  rectall(1, 1, hilite);
}


/* Draw dot a little larger than just a single pixel at specified location. */

drawspot(x, y, o)
int x, y;
bit o;
{
  drawpoint(x, y, o);
  drawpoint(x, y-1, o);
  drawpoint(x-1, y, o);
  drawpoint(x+1, y, o);
  drawpoint(x, y+1, o);
}


/* Draw the local horizon, and draw in the planets where they are at the */
/* time in question, as done when the -Z is combined with the -X switch. */

xcharthorizon()
{
  double lon, lat,
    lonz[TOTAL+1], latz[TOTAL+1], azi[TOTAL+1], alt[TOTAL+1];
  int x[TOTAL+1], y[TOTAL+1], m[TOTAL+1], n[TOTAL+1],
    cx = chartx / 2, cy = charty / 2, unit = 12*scale/100,
    x1, y1, x2, y2, xs, ys, i, j, k, l;
  rectall(1, 1, hilite);

  /* Make a slightly smaller rectangle within the window to draw the planets */
  /* in. Make segments on all four edges marking 5 degree increments.        */

  x1 = y1 = unit/2; x2 = chartx-x1; y2 = charty-y1;
  xs = x2-x1; ys = y2-y1;
  for (i = 0; i < 180; i += 5) {
    j = y1+(int)((double)(i*ys)/180.0);
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0);
    drawline(x1+1, j, x1+1+k, j, hilite, 0);
    drawline(x2-1, j, x2-1-k, j, hilite, 0);
  }
  for (i = 0; i < 360; i += 5) {
    j = x1+(int)((double)(i*xs)/DEGREES);
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0);
    drawline(j, y1+1, j, y1+1+k, hilite, 0);
    drawline(j, y2-1, j, y2-1-k, hilite, 0);
  }

  /* Draw vertical lines dividing our rectangle into four areas. In our     */
  /* local space chart, the middle line represents due south, the left line */
  /* due east, the right line due west, and the edges due north. A fourth   */
  /* horizontal line divides that which is above and below the horizon.     */

  drawline(cx, y1, cx, y2, gray, 1);
  drawline((cx+x1)/2, y1, (cx+x1)/2, y2, gray, 1);
  drawline((cx+x2)/2, y1, (cx+x2)/2, y2, gray, 1);
  edge(x1, y1, x2, y2, on);
  drawline(x1, cy, x2, cy, on, 1);

  /* Calculate the local horizon coordinates of each planet. First convert */
  /* zodiac position and declination to zenith longitude and latitude.     */

  lon = DTOR(mod(Lon)); lat = DTOR(Lat);
  for (i = 1; i <= total; i++) {
    lonz[i] = DTOR(planet[i]); latz[i] = DTOR(planetalt[i]);
    ecltoequ(&lonz[i], &latz[i]);
  }
  for (i = 1; i <= total; i++) if (proper(i)) {
    lonz[i] = DTOR(mod(RTOD(lonz[18]-lonz[i]+PI/2.0)));
    equtolocal(&lonz[i], &latz[i], PI/2.0-lat);
    azi[i] = DEGREES-RTOD(lonz[i]); alt[i] = RTOD(latz[i]);
    x[i] = x1+(int)((double)xs*(mod(90.0-azi[i]))/DEGREES+0.5);
    y[i] = y1+(int)((double)ys*(90.0-alt[i])/180.0+0.5);
    m[i] = x[i]; n[i] = y[i]+unit/2;
  }

  /* As in the drawglobe() routine, we now determine where to draw the   */
  /* glyphs in relation to the actual points, so that the glyphs aren't  */
  /* drawn on top of each other if possible. Again, we assume that we'll */
  /* put the glyph right under the point, unless there would be some     */
  /* overlap and the above position is better off.                       */

  for (i = 1; i <= total; i++) if (proper(i)) {
    k = l = chartx+charty;
    for (j = 1; j < i; j++) if (proper(j)) {
      k = MIN(k, abs(m[i]-m[j])+abs(n[i]-n[j]));
      l = MIN(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j]));
    }
    if (k < unit || l < unit)
      if (k < l)
	n[i] -= unit;
  }
  for (i = total; i >= 1; i--) if (proper(i))    /* Draw planet's glyph. */
    drawobject(i, m[i], n[i]);
  for (i = total; i >= 1; i--) if (proper(i)) {
#ifdef X11
    if (!xbitmap)
      Xcolor(objectcolor[i]);
#endif
    if (!bonus || i > BASE)
      drawpoint(x[i], y[i], objectcolor[i]);    /* Draw small or large dot */
    else                                        /* near glyph indicating   */
      drawspot(x[i], y[i], objectcolor[i]);     /* exact local location.   */
  }
}


/* Draw the local horizon, and draw in the planets where they are at the  */
/* time in question. This chart is done when the -Z0 is combined with the */
/* -X switch. This is an identical function to xcharthorizon(); however,  */
/* that routine's chart is entered on the horizon and meridian. Here we   */
/* center the chart around the center of the sky straight up from the     */
/* local horizon, with the horizon itself being an encompassing circle.   */

xchartsky()
{
  double lon, lat, rx, ry, s, a, sqr2,
    lonz[TOTAL+1], latz[TOTAL+1], azi[TOTAL+1], alt[TOTAL+1];
  int x[TOTAL+1], y[TOTAL+1], m[TOTAL+1], n[TOTAL+1],
    cx = chartx / 2, cy = charty / 2, unit = 12*scale/100, i, j, k, l;

  /* Draw a circle in window to indicate horizon line, lines dividing   */
  /* the window into quadrants to indicate n/s and w/e meridians, and   */
  /* segments on these lines and the edges marking 5 degree increments. */

  initcircle();
  sqr2 = sqrt(2.0);
  drawline(cx, 0, cx, charty-1, gray, 1);
  drawline(0, cy, chartx-1, cy, gray, 1);
  for (i = -125; i <= 125; i += 5) {
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0);
    s = 1.0/(90.0*sqr2);
    j = cy+(int)(s*cy*i);
    drawline(cx-k, j, cx+k, j, hilite, 0);
    j = cx+(int)(s*cx*i);
    drawline(j, cy-k, j, cy+k, hilite, 0);
  }
  for (i = 5; i < 55; i += 5) {
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0);
    s = 1.0/(180.0-90.0*sqr2);
    j = (int)(s*cy*i);
    drawline(0, j, k, j, hilite, 0);
    drawline(0, charty-1-j, k, charty-1-j, hilite, 0);
    drawline(chartx-1, j, chartx-1-k, j, hilite, 0);
    drawline(chartx-1, charty-1-j, chartx-1-k, charty-1-j, hilite, 0);
    j = (int)(s*cx*i);
    drawline(j, 0, j, k, hilite, 0);
    drawline(chartx-1-j, 0, chartx-1-j, k, hilite, 0);
    drawline(j, charty-1, j, charty-1-k, hilite, 0);
    drawline(chartx-1-j, charty-1, chartx-1-j, charty-1-k, hilite, 0);
  }
  rx = cx/sqr2; ry = cy/sqr2;
  for (i = 0; i < 360; i++)
    drawline(cx+(int)(rx*circx[i]), cy+(int)(ry*circy[i]),
	     cx+(int)(rx*circx[i+1]), cy+(int)(ry*circy[i+1]), on, 0);
  for (i = 0; i < 360; i += 5) {
    k = 2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0);
    drawline(cx+(int)((rx-k)*circx[i]), cy+(int)((ry-k)*circy[i]),
	     cx+(int)((rx+k)*circx[i]), cy+(int)((ry+k)*circy[i]), on, 0);
  }

  /* Calculate the local horizon coordinates of each planet. First convert */
  /* zodiac position and declination to zenith longitude and latitude.     */

  lon = DTOR(mod(Lon)); lat = DTOR(Lat);
  for (i = 1; i <= total; i++) {
    lonz[i] = DTOR(planet[i]); latz[i] = DTOR(planetalt[i]);
    ecltoequ(&lonz[i], &latz[i]);
  }
  for (i = 1; i <= total; i++) if (proper(i)) {
    lonz[i] = DTOR(mod(RTOD(lonz[18]-lonz[i]+PI/2.0)));
    equtolocal(&lonz[i], &latz[i], PI/2.0-lat);
    azi[i] = a = DEGREES-RTOD(lonz[i]); alt[i] = 90.0-RTOD(latz[i]);
    s = alt[i]/90.0;
    x[i] = cx+(int)(rx*s*COSD(180.0+azi[i])+0.5);
    y[i] = cy+(int)(ry*s*SIND(180.0+azi[i])+0.5);
    if (!ISLEGAL(x[i], y[i]))
      x[i] = -1000;
    m[i] = x[i]; n[i] = y[i]+unit/2;
  }

  /* As in the drawglobe() routine, we now determine where to draw the   */
  /* glyphs in relation to the actual points, so that the glyphs aren't  */
  /* drawn on top of each other if possible. Again, we assume that we'll */
  /* put the glyph right under the point, unless there would be some     */
  /* overlap and the above position is better off.                       */

  for (i = 1; i <= total; i++) if (proper(i)) {
    k = l = chartx+charty;
    for (j = 1; j < i; j++) if (proper(j)) {
      k = MIN(k, abs(m[i]-m[j])+abs(n[i]-n[j]));
      l = MIN(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j]));
    }
    if (k < unit || l < unit)
      if (k < l)
	n[i] -= unit;
  }
  for (i = total; i >= 1; i--) if (m[i] >= 0 && proper(i))    /* Draw glyph. */
    drawobject(i, m[i], n[i]);
  for (i = total; i >= 1; i--) if (x[i] >= 0 && proper(i)) {
#ifdef X11
    if (!xbitmap)
      Xcolor(objectcolor[i]);
#endif
    if (!bonus || i > BASE)
      drawpoint(x[i], y[i], objectcolor[i]);    /* Draw small or large dot */
    else                                        /* near glyph indicating   */
      drawspot(x[i], y[i], objectcolor[i]);     /* exact local location.   */
  }
  rectall(1, 1, hilite);
}


/* Given an x and y coordinate, return the angle formed by the line from the */
/* origin to this coordinate. This is just converting from rectangular to    */
/* polar coordinates; however, we don't determine the radius here.           */

double angle(x, y)
double x, y;
{
  double a;
  a = atan(y/x);
  if (a < 0.0)
    a += PI;
  if (y < 0.0)
    a += PI;
  return mod(RTOD(a));
}


/* Draw a chart depicting an aerial view of the solar system in space, with */
/* all the planets drawn around the Sun, and the specified central planet   */
/* in the middle, as done when the -S is combined with the -X switch.       */

xchartspace()
{
  int x[TOTAL+1], y[TOTAL+1], m[TOTAL+1], n[TOTAL+1],
    cx = chartx / 2, cy = charty / 2, unit = 12*scale/100, i, j, k, l;
  double sx, sy, sz = 30.0, x1, y1, a;

  /* Determine the scale of the window. For a scale size of 300, make    */
  /* the window 6 AU in radius (enough for inner planets out to asteroid */
  /* belt). For a scale of 200, make window 30 AU in radius (enough for  */
  /* planets out to Neptune). For scale of 100, make it 90 AU in radius  */
  /* (enough for all planets including the orbits of the uranians.)      */

  if (scale < 200)
    sz = 90.0;
  else if (scale > 200)
    sz = 6.0;
  sx = (double)cx/sz; sy = (double)cy/sz;
  for (i = 0; i <= total; i++) if (proper(i)) {

    /* Determine what glyph corresponds to our current planet. Normally the */
    /* array indices are the same, however we have to do some swapping for  */
    /* non-geocentric based charts where a planet gets replaced with Earth. */

    if (centerplanet == 0)
      j = i < 2 ? 1-i : i;
    else if (centerplanet == 1)
      j = i;
    else
      j = i == 0 ? centerplanet : (i == centerplanet ? 0 : i);
    x1 = spacex[j]; y1 = spacey[j];
    x[i] = cx-(int)(x1*sx); y[i] = cy+(int)(y1*sy);
    m[i] = x[i]; n[i] = y[i]+unit/2;
  }

  /* As in the drawglobe() routine, we now determine where to draw the   */
  /* glyphs in relation to the actual points, so that the glyphs aren't  */
  /* drawn on top of each other if possible. Again, we assume that we'll */
  /* put the glyph right under the point, unless there would be some     */
  /* overlap and the above position is better off.                       */

  for (i = 0; i <= total; i++) if (proper(i)) {
    k = l = chartx+charty;
    for (j = 0; j < i; j++) if (proper(j)) {
      k = MIN(k, abs(m[i]-m[j])+abs(n[i]-n[j]));
      l = MIN(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j]));
    }
    if (k < unit || l < unit)
      if (k < l)
	n[i] -= unit;
  }

  /* Draw the 12 sign boundaries from the center body to edges of screen. */

  a = mod(angle(spacex[3], spacey[3])-planet[3]);
  for (i = 0; i < SIGNS; i++) {
    k = cx+2*(int)((double)cx*COSD((double)i*30.0+a));
    l = cy+2*(int)((double)cy*SIND((double)i*30.0+a));
    drawclip(cx, cy, k, l, gray, 1);
  }
  for (i = total; i >= 0; i--) if (proper(i) && ISLEGAL(m[i], n[i]))
    drawobject(i, m[i], n[i]);
  for (i = total; i >= 0; i--) if (proper(i) && ISLEGAL(x[i], y[i])) {
#ifdef X11
    if (!xbitmap)
      Xcolor(objectcolor[i]);
#endif
    if (!bonus || i > BASE)
      drawpoint(x[i], y[i], objectcolor[i]);    /* Draw small or large dot */
    else                                        /* near glyph indicating   */
      drawspot(x[i], y[i], objectcolor[i]);     /* exact local location.   */
  }
  rectall(1, 1, hilite);
}


/* Create a chart in the window based on the current graphics chart mode. */
/* This is the main dispatch routine for all of the program's graphics.   */

void xchart()
{
  int i, j;
  if (xbitmap)
    block(0, 0, chartx - 1, charty - 1, off);    /* Clear bitmap screen. */
  switch (modex) {
  case 'c':
    if (relation != -1)
      xchartwheel();
    else
      xchartwheelrelation();
    break;
  case 'l':
    drawworld(degree);     /* First draw map of world.           */
    xchartastrograph();    /* Then draw astro-graph lines on it. */
    break;
  case 'a':
    if (relation != -1)
      xchartaspectgrid();
    else
      xchartrelationaspectgrid();
    break;
  case 'z':
    if (todisplay & DASHZ0)
      xchartsky();
    else
      xcharthorizon();
    break;
  case 's':
    xchartspace();
    break;
  case 'w':
    drawworld(degree);                          /* First draw map of world. */
    if (bonus && (todisplay & DASHXW0) == 0)    /* Then maybe Ley lines.    */
      drawleylines(degree);
    break;
  case 'p':
  case 'g':
    drawglobe(degree);
    break;
  }

  /* Print text showing chart information at bottom of window. */

  if (xtext && modex != 'w' && modex != 'p' && modex != 'g') {
    if (Mon == -1)
      sprintf(string, "(no time or space)");
    else if (relation == 2)
      sprintf(string, "(composite)");
    else {
      i = (int) Mon; j = (int) ((Tim-floor(Tim))*100.0+0.1);
      sprintf(string, "%2.0f %c%c%c %.0f %2.0f:%d%d (%.2f GMT) %s",
	      Day, monthname[i][0], monthname[i][1], monthname[i][2], Yea,
	      floor(Tim), j/10, j%10, -Zon, stringlocation(Lon, Lat, 100.0));
    }
    drawtext(string, (chartx-strlen(string)*FONTX)/2, charty-3, on);
  }
}
#endif

/**/
Newsgroups: alt.astrology