/*                                                               -*- C -*-
** Astrolog (Version 4.40) File: xcharts0.c
**
** IMPORTANT NOTICE: The graphics database and chart display routines
** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
** (astara@u.washington.edu). 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.
**
** The main planetary calculation routines used in this program have
** been Copyrighted and the core of this program is basically a
** conversion to C of the routines created by James Neely as listed in
** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
** available from Matrix Software. The copyright gives us permission to
** use the routines for personal use but not to sell them or profit from
** them in any way.
**
** The PostScript code within the core graphics routines are programmed
** and Copyright (C) 1992-1993 by Brian D. Willoughby
** (brianw@sounds.wa.com). Conditions are identical to those above.
**
** The extended accurate ephemeris databases and formulas are from the
** calculation routines in the program "Placalc" and are programmed and
** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
** (alois@azur.ch). The use of that source code is subject to
** regulations made by Astrodienst Zurich, and the code is not in the
** public domain. This copyright notice must not be changed or removed
** by any user of this program.
**
** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
** X Window graphics initially programmed 10/23-29/1991.
** PostScript graphics initially programmed 11/29-30/1992.
** Last code change made 1/29/1995.
*/

/* $VER: $Id: xcharts0.c,v 1.3 1995/07/02 22:22:23 tf Exp $ */

#include "astrolog.h"


#ifdef GRAPH
/*
******************************************************************************
** Subchart Graphics Routines.
******************************************************************************
*/

/* Given a string, draw it on the screen using the given color. The       */
/* position of the text is based the saved positions of where we drew the */
/* text the last time the routine was called, being either directly below */
/* in the same column or in the same row just to the right. This is used  */
/* by the sidebar drawing routine to print a list of text on the chart.   */

/* Examples:

     DrawPrint(NULL, x,y);       Initialize the cursor to position (x,y)

     DrawPrint("bla",

*/



int DrawPrint(sz, m, n)
char *sz;
int m, n;
{
  static int x0, x, y;

  if (sz == NULL)      /* Null string means just initialize position. */
  {
    x0 = x = m; y = n;
    return y;
  }

  if (y >= gs.yWin-1)  /* Don't draw if we've scrolled off the chart bottom. */
    return y;

  DrawColor(m);
  DrawSz(sz, x, y, dtLeft | dtBottom);

  /* If the second parameter is TRUE, we stay on the same line, otherwise */
  /* when FALSE we go to the next line at the original column setting.    */

  if (n)
    x += CchSz(sz)*xFont*gi.nScaleT;

  else
  {
    x = x0;
    n = y;
    y += yFont*gi.nScaleT;
  }
  return y;
}


/* Print text showing the chart information and house and planet positions */
/* of a chart in a "sidebar" to the right of the chart in question. This   */
/* is always done for the -v and -w graphic wheel charts unless the -v0    */
/* switch flag is also set, in which case none of the stuff here is done.  */

void DrawInfo()
{
  char sz[cchSzDef];
  ET et;
  int i, y, a, s;

#ifdef INTERPRET
  int tot, abo, lef;

  /* Hack: Just for fun, if interpretation is active (which normally has  */
  /* no effect whatsoever on graphics) we'll decorate the chart a little. */

  if (us.fInterpret)
  {
    if (us.nScreenWidth & 1)
    {
      /* If screenwidth value is odd, draw a moire pattern in each corner. */

      abo = gs.yWin/(us.nScreenWidth/10);
      lef = gs.xWin/(us.nScreenWidth/10);

      for (y = 0; y <= 1; y++)
      {
        for (i = 0; i <= 1; i++)
        {
          for (s = 0; s <= 1; s++)
          {
            for (a = 1; a < (s ? lef : abo)*2; a++)
            {
              DrawColor(a & 1 ? gi.kiGray : gi.kiOff);
              DrawLine(i ? gs.xWin-1-lef : lef, y ? gs.yWin-1-abo : abo,
                s ? (i ? gs.xWin-1-a : a) : i*(gs.xWin-1),
                s ? y*(gs.yWin-1) : (y ? gs.yWin-1-a : a));
            }
          }
        }
      }
    } 

    else
    {
      /* If screenwidth is even, draw spider web lines in each corner. */

      DrawColor(gi.kiGray);

      tot = us.nScreenWidth*3/20;
      abo = gs.yWin/4;
      lef = gs.xWin/4;

      for (y = 0; y <= 1; y++)
      {
        for (i = 0; i <= 1; i++)
        {
          for (a = 1; a < tot; a++)
          {
            DrawLine(i*(gs.xWin-1), y ? (gs.yWin-1-a*abo/tot) : a*abo/tot,
              i ? gs.xWin-1-lef+a*lef/tot : lef-a*lef/tot, y*(gs.yWin-1));
          }
        }
      }
    }
  }
#endif

  if (!gs.fText || us.fVelocity)    /* Don't draw sidebar if */
    return;                         /* -v0 flag is set.      */

  a = us.fAnsi;
  us.fAnsi = -(!gs.fFont || (!gs.fMeta && !gs.fPS));
  DrawColor(gi.kiLite);

  if (gs.fBorder)
    DrawLine(gs.xWin-1, 0, gs.xWin-1, gs.yWin-1);

  gs.xWin += xSideT;
  DrawPrint(NULL, gs.xWin-xSideT+xFontT-gi.nScaleT, yFont*7/5*gi.nScaleT);

  /* Print chart header and setting information. */

  sprintf(sz, "%s %s", szAppName, szVersionCore);
  DrawPrint(sz, gi.kiOn, fFalse);

  if (*ciMain.nam)
    DrawPrint(ciMain.nam, gi.kiLite, fFalse);

  if (Mon == -1)
    sprintf(sz, "No time or space.");

  else if (us.nRel == rcComposite)
    sprintf(sz, "Composite chart.");

  else
  {
    sprintf(sz, "%c%c%c %s", chDay3(DayOfWeek(Mon, Day, Yea)),  SzDate(Mon, Day, Yea, fTrue));
    DrawPrint(sz, gi.kiLite, fFalse);
    DrawPrint(SzTim(Tim), gi.kiLite, fTrue);
    sprintf(sz, " (%cT %s GMT)", Dst != 0.0 ? 'D' : 'S', SzZone(Zon));
  }

  DrawPrint(sz, gi.kiLite, fFalse);

  if (*ciMain.loc)
    DrawPrint(ciMain.loc, gi.kiLite, fFalse);

  DrawPrint(SzLocation(Lon, Lat), gi.kiLite, fFalse);
  sprintf(sz, "%s houses.", szSystem[us.nHouseSystem]);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "%s, %s.", us.fSiderial ? "Sidereal" : "Tropical",
                         us.objCenter == 0 ? "Heliocentric" :
                        (us.objCenter == 1 ? "Geocentric" : szObjName[us.objCenter]));

  DrawPrint(sz, gi.kiLite, fFalse);
  sprintf(sz, "Julian Day = %11.4f", JulianDayFromTime(T));
  DrawPrint(sz, gi.kiLite, fFalse);

  /* Print house cusp positions. */

  DrawPrint("", gi.kiLite, fFalse);

  for (i = 1; i <= cSign; i++)
  {
    sprintf(sz, "%2d%s house: ", i, szSuffix[i]);
    y = DrawPrint(sz, kSignB(i), fTrue);

    if (!is.fSeconds && (gs.nScale == 100 || !gs.fFont || !gi.fFile || gs.fBitmap) && y < gs.yWin-1)
    {
      s = gi.nScale;
      gi.nScale = gi.nScaleT;
      DrawSign(SFromZ(house[i]), gs.xWin-12*gi.nScaleT,
        y-(yFont/2-1)*gi.nScaleT);
      gi.nScale = s;
    }

    DrawPrint(SzZodiac(house[i]), kSignB(SFromZ(house[i])), fFalse);
  }

  /* Print planet positions. */

  DrawPrint("", gi.kiLite, fFalse);

  for (i = 1; i <= oNorm; i++) if (!ignore[i] && !FCusp(i))
  {
    sprintf(sz, is.fSeconds ? "%3.3s: " : "%4.4s: ", szObjName[i]);
    DrawPrint(sz, kObjB[i], fTrue);
    y = DrawPrint(SzZodiac(planet[i]), kSignB(SFromZ(planet[i])), fTrue);

    if (!is.fSeconds && i < starLo && (gs.nScale == 100 || !gs.fFont || !gi.fFile || gs.fBitmap) && y < gs.yWin-1)
    {
      s = gi.nScale;
      gi.nScale = gi.nScaleT;
      DrawObject(i, gs.xWin-12*gi.nScaleT, y-(yFont/2-1)*gi.nScaleT);
      gi.nScale = s;
    }

    sprintf(sz, "%c ", ret[i] < 0.0 ? chRet : ' ');
    s = FThing(i);
    DrawPrint(sz, gi.kiOn, s);

    if (s)
    {
      is.fSeconds = fFalse;
      DrawPrint(SzAltitude(planetalt[i]), gi.kiLite, fFalse);
      is.fSeconds = us.fSeconds;
    }
  }

  /* Print star positions. */

  for (i = starLo; i <= starHi; i++) if (!ignore[i])
  {
    s = oNorm+starname[i-oNorm];
    sprintf(sz, is.fSeconds ? "%3.3s: " : "%4.4s: ", szObjName[s]);
    DrawPrint(sz, kObjB[s], fTrue);
    DrawPrint(SzZodiac(planet[s]), kSignB(SFromZ(planet[s])), fTrue);
    DrawPrint("  ", gi.kiOn, fTrue);
    DrawPrint(SzAltitude(planetalt[s]), gi.kiLite, fFalse);
  }

  /* Print element table information. */

  DrawPrint("", gi.kiLite, fFalse);
  CreateElemTable(&et);

  sprintf(sz, "Fire: %d, Earth: %d,", et.coElem[eFir], et.coElem[eEar]);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "Air : %d, Water: %d", et.coElem[eAir], et.coElem[eWat]);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "Car: %d, Fix: %d, Mut: %d", et.coMode[0], et.coMode[1], et.coMode[2]);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "Yang: %d, Yin: %d", et.coYang, et.coYin);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "M: %d, N: %d, A: %d, D: %d", et.coMC, et.coIC, et.coAsc, et.coDes);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "Ang: %d, Suc: %d, Cad: %d", et.coModeH[0], et.coModeH[1], et.coModeH[2]);
  DrawPrint(sz, gi.kiLite, fFalse);

  sprintf(sz, "Learn: %d, Share: %d", et.coLearn, et.coShare);
  DrawPrint(sz, gi.kiLite, fFalse);

  us.fAnsi = a;
}


/*
******************************************************************************
** Map Chart Routines.
******************************************************************************
*/

/* Another stream reader, this one is used by the globe drawing routine: */
/* for the next body of land/water, return its name (and color), its     */
/* longitude and latitude, and a vector description of its outline.      */

bool FReadWorldData(nam, loc, lin)
char FAR **nam, FAR **loc, FAR **lin;
{
  static char FAR **psz = (char FAR **)szWorldData;
  int i;

  *loc = *psz++;
  *lin = *psz++;
  *nam = *psz++;

  if (*loc[0])
  {
    if (gs.fPrintMap && gi.fFile)
    {
      i = **nam - '0';
      AnsiColor(i ? kRainbowA[i] : kMainA[7]);
      PrintSz(*nam+1); PrintL();
    }
    return fTrue;
  }
  psz = (char FAR **)szWorldData;  /* Reset stream when no data left. */
  return fFalse;
}


/* 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. */

bool FGlobeCalc(x1, y1, u, v, cx, cy, rx, ry, deg)
real x1, y1;
int *u, *v, cx, cy, rx, ry, deg;
{
  real j, siny1;

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

  if (gi.nMode == gGlobe)
  {
    x1 = Mod(x1+(real)deg);    /* Shift by current globe rotation value. */

    if (gs.rTilt != 0.0)
    {
      /* Do another coordinate shift if the globe's equator is tilted any. */

      x1 = RFromD(x1); y1 = RFromD(rDegQuad-y1);
      CoorXform(&x1, &y1, RFromD(gs.rTilt));
      x1 = Mod(DFromR(x1)); y1 = rDegQuad-DFromR(y1);
    }
    *v = cy + (int)((real)ry*-RCosD(y1)-rRound);
    *u = cx + (int)((real)rx*-RCosD(x1)*RSinD(y1)-rRound);
    return x1 > rDegHalf;
  }

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

  siny1 = RSinD(y1);
  j = gs.fAlt ? rDegQuad+x1+deg : 270.0-x1-deg;
  *v = cy + (int)(siny1*(real)ry*RSinD(j)-rRound);
  *u = cx + (int)(siny1*(real)rx*RCosD(j)-rRound);
  return gs.fAlt ? y1 < rDegQuad : y1 > rDegQuad;
}


/* 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.  */

void DrawLeyLine(l1, f1, l2, f2)
real l1, f1, l2, f2;
{
  l1 = Mod(l1); l2 = Mod(l2);

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

  f1 = rDegQuad-RAsin(f1)/rPiHalf*rDegQuad;
  f2 = rDegQuad-RAsin(f2)/rPiHalf*rDegQuad;

  DrawWrap((int)(l1*(real)gi.nScale+rRound)+1,
           (int)(f1*(real)gi.nScale+rRound)+1,
           (int)(l2*(real)gi.nScale+rRound)+1,
           (int)(f2*(real)gi.nScale+rRound)+1, 1, gs.xWin-2);
}


/* 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.    */

void DrawLeyLines(deg)
int deg;
{
  real off = (real)deg, phi, h, h1, h2, r, i;

  phi = (RSqr(5.0)+1.0)/2.0;                   /* Icosahedron constants. */
  h = 1.0/(phi*2.0-1.0);
  DrawColor(kMainB[6]);

  for (i = off; i < rDegMax+off; i += 72.0)    /* Draw icosahedron edges. */
  {
    DrawLeyLine(i,       h, i+72.0,  h);
    DrawLeyLine(i-36.0, -h, i+36.0, -h);
    DrawLeyLine(i,       h, i,       1.0);
    DrawLeyLine(i+36.0, -h, i+36.0, -1.0);
    DrawLeyLine(i,       h, i+36.0, -h);
    DrawLeyLine(i,       h, i-36.0, -h);
  }

  r = 1.0/RSqr(3.0)/phi/RCos(RFromD(54.0));    /* Dodecahedron constants. */
  h2 = RSqr(1.0-r*r); h1 = h2/(phi*2.0+1.0);
  DrawColor(kMainB[4]);

  for (i = off; i < rDegMax+off; i += 72.0)    /* Draw docecahedron edges. */
  {
    DrawLeyLine(i-36.0,  h2, i+36.0,  h2);
    DrawLeyLine(i,      -h2, i+72.0, -h2);
    DrawLeyLine(i+36.0,  h2, i+36.0,  h1);
    DrawLeyLine(i,      -h2, i,      -h1);
    DrawLeyLine(i+36.0,  h1, i+72.0, -h1);
    DrawLeyLine(i+36.0,  h1, i,      -h1);
  }
}


/* This major routine draws all of Astrolog's map charts. This means       */
/* either the world map or the constellations, in either rectangular or    */
/* globe hemisphere form. The rectangular chart may also be done in a      */
/* Mollewide projection, for six total combinations. We shift the chart by */
/* specified rotational and tilt values, and may draw on the chart each    */
/* planet at its zenith position on Earth or location in constellations.   */

void DrawMap(fSky, fGlobe, deg)
bool fSky, fGlobe;
int deg;
{
  char *nam, *loc, *lin, chCmd;
  int X[objMax], Y[objMax], M[objMax], N[objMax],
    cx = gs.xWin/2,
    cy = gs.yWin/2,
    rx, ry, lon, lat, unit = 12*gi.nScale,
    x, y,
    xold, yold,
    m, n, u, v, i, j, k, l,
    nScl = gi.nScale;

  bool fNext = fTrue, fCan;
  real planet1[objMax], planet2[objMax], x1, y1, rT;

#ifdef CONSTEL
  char *pch;
  bool fBlank;
  int isz = 0, nC, xT, yT, xDelta, yDelta, xLo, xHi, yLo, yHi;
#endif

  /* Set up some variables. */

  rx = cx-1; ry = cy-1;

  if (fGlobe)
    fCan = (gs.rTilt == 0.0 && gi.nMode != gPolar);

#ifdef CONSTEL

  /* Draw a dot grid for large rectangular constellation charts. */

  if (fSky && !fGlobe && !gs.fMollewide && gi.nScale/gi.nScaleT > 2)
  {
    for (yT = 5; yT < nDegHalf; yT += 5)
    {
      for (xT = 5; xT <= nDegMax; xT += 5)
      {
        DrawColor(xT % 15 == 0 && yT % 10 == 0 ? gi.kiOn : gi.kiGray);

        x = xT+deg;

        if (x > nDegMax)
          x -= nDegMax;

        DrawPoint(x*nScl, yT*nScl);
      }
    }
  }
#endif

  loop
  {
    /* Get the next chunk of data to process. Get the starting position, */
    /* map it to the screen, and set the drawing color appropriately.    */

    if (fNext)
    {
      fNext = fFalse;

      /* For constellations, get data for the next constellation shape. */

      if (fSky)
      {

#ifdef CONSTEL
        isz++;
        if (isz > cCnstl)
          break;

        DrawColor(gs.fAlt && gi.nMode != gPolar && (gi.nMode != gWorldMap || !gs.fMollewide) ? kMainB[7] : kRainbowB[6]);

        pch = (char *)szDrawConstel[isz];
        lon = nDegMax - (((pch[2]-'0')*10+(pch[3]-'0'))*15+(pch[4]-'0')*10+(pch[5]-'0'));
        lat = 90-((pch[6] == '-' ? -1 : 1)*((pch[7]-'0')*10+(pch[8]-'0')));
        pch += 9;

        xLo = xHi = xT = xold = x = lon;
        yLo = yHi = yT = yold = y = lat;
        nC = 0;

        if (fGlobe)
        {
          FGlobeCalc((real)x, (real)y, &m, &n, cx, cy, rx, ry, deg);
          k = l = fTrue;
        }
        else 
        {
          xold += deg;
          x += deg;
        }
#else
        ;
#endif
      }

      /* For world maps, get data for the next coastline piece. */

      else /* !fSky */
      {
        if (!FReadWorldData(&nam, &loc, &lin))
          break;

        i = nam[0]-'0';

        DrawColor((!fGlobe && gi.nMode == gAstroGraph) ? gi.kiOn :
          (gi.nMode == gGlobe && gs.fAlt) ? gi.kiGray :
          (i ? kRainbowB[i] : kMainB[7]));

        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'));

        if (fGlobe)
        {
          x = 180-lon;
          y = 90-lat;
          FGlobeCalc((real)x, (real)y, &m, &n, cx, cy, rx, ry, deg);
          k = l = fTrue;
        }
        else
        {
          xold = x = 181-lon+deg;
          yold = y = 91-lat;
        }
      }
    }

    /* Get the next unit from the string to draw on the screen as a line. */

    if (fSky)
    {
      /* For constellations we have a cache of how long we should keep    */
      /* going in the previous direction, as say "u5" for up five should  */
      /* move our pointer up five times without advancing string pointer. */

#ifdef CONSTEL
      if (nC <= 0)
      {
        if (!(chCmd = *pch))
        {
          fNext = fTrue;
          if (gs.fText)
          {
            /* If we've reached the end of current constellation, compute */
            /* the center location in it based on lower and upper bounds  */
            /* we've maintained, and print the name of the constel there. */

            xT = xLo + (xHi - xLo)*(szDrawConstel[isz][0]-'1')/8;
            yT = yLo + (yHi - yLo)*(szDrawConstel[isz][1]-'1')/8;

            if (xT < 0)
              xT += nDegMax;
            else if (xT > nDegMax)
              xT -= nDegMax;

            if (fGlobe)
            {
              if (FGlobeCalc((real)xT, (real)yT, &x, &y, cx, cy, rx, ry, deg))
                continue;
            }
            else
            {
              xT += deg;
              if (xT > nDegMax)
                xT -= nDegMax;

              if (gs.fMollewide)
                x = 180*nScl + NMultDiv(xT-180, NMollewide(yT-91), 180L);
              else
                x = xT*nScl;

              y = yT*nScl;
            }
            DrawColor(gs.fAlt && gi.nMode != gPolar && (gi.nMode != gWorldMap || !gs.fMollewide) ? gi.kiGray : kMainB[5]);
            DrawSz(szCnstlAbbrev[isz], x, y, dtCent);
          }
          continue;
        }
        pch++;

        /* Get the next direction and distance from constellation string. */

        if (fBlank = (chCmd == 'b'))
          chCmd = *pch++;

        xDelta = yDelta = 0;

        switch (chCmd) {
        case 'u': yDelta = -1;                      break;    /* Up    */
        case 'd': yDelta =  1;                      break;    /* Down  */
        case 'l': xDelta = -1;                      break;    /* Left  */
        case 'r': xDelta =  1;                      break;    /* Right */
        case 'U': yDelta = -1; nC = (yT-1)%10+1;    break;    /* Up until    */
        case 'D': yDelta =  1; nC = 10-yT%10;       break;    /* Down until  */
        case 'L': xDelta = -1; nC = (xT+599)%15+1;  break;    /* Left until  */
        case 'R': xDelta =  1; nC = 15-(xT+600)%15; break;    /* Right until */
        default: PrintError("Bad draw.");                     /* Shouldn't happen. */
        }
        if (chCmd >= 'a')
          nC = NFromPch(&pch);    /* Figure out how far to draw. */
      }
      nC--;

      xT += xDelta; x += xDelta;
      yT += yDelta; y += yDelta;

      if (fBlank)
      {
        xold = x; yold = y;    /* We occasionally want to move the pointer */
        l = fFalse;            /* without drawing the line on the screen.  */
        continue;
      }

      if (xT < xLo)         /* Maintain our bounding rectangle for this */
        xLo = xT;           /* constellation if we crossed over it any. */
      else if (xT > xHi)
        xHi = xT;

      if (yT < yLo)
        yLo = yT;
      else if (yT > yHi)
        yHi = yT;
#else
      ;
#endif
    }

    else /* !fSky */
    {
      /* Get the next unit from the much simpler world map strings. */

      if (!(chCmd = *lin))
      {
        fNext = fTrue;
        continue;
      }
      lin++;

      /* Each unit is exactly one character in the coastline string. */

      if (chCmd == 'L' || chCmd == 'H' || chCmd == 'G')
        x--;
      else if (chCmd == 'R' || chCmd == 'E' || chCmd == 'F')
        x++;

      if (chCmd == 'U' || chCmd == 'H' || chCmd == 'E')
        y--;
      else if (chCmd == 'D' || chCmd == 'G' || chCmd == 'F')
        y++;
    }

    /* Transform map coordinates to screen coordinates and draw a line. */

    while (x >= nDegMax)    /* Take care of coordinate wrap around. */
      x -= nDegMax;

    while (x < 0)
      x += nDegMax;

    if (abs(x-xold) > nDegHalf)
      xold = x;

    if (fGlobe)
    {
      /* For globes, we have to go do a complicated transformation, and not */
      /* draw when we're hidden on the back side of the sphere. We're smart */
      /* and try to only do the slow stuff when we know we'll be visible.   */

      if (fCan)
      {
        k = x+deg;
        if (k >= nDegMax)
          k -= nDegMax;
        k = (k <= 180);
      }

      if (k && !FGlobeCalc((real)x, (real)y, &u, &v, cx, cy, rx, ry, deg))
      {
        if (l)
          DrawLine(m, n, u, v);

        m = u; n = v;
        l = fTrue;
      }
      else
        l = fFalse;
    }

    else /* !fGlobe */
    {
      /* Rectangular maps are much simpler, with screen coordinates      */
      /* proportional to internal coords. For the Mollewide projection   */
      /* we have to apply a factor to the horizontal positioning though. */

      if (gs.fMollewide && gi.nMode != gAstroGraph)
      {
        DrawLine(180*nScl + NMultDiv(xold-180, NMollewide(yold-91), 180L), yold*nScl,
                 180*nScl + NMultDiv(x-180,    NMollewide(y-91),    180L), y*nScl);
      }
      else DrawLine(xold*nScl, yold*nScl, x*nScl, y*nScl);
      xold = x; yold = y;
    }
  }

  /* Draw the outline of the map, either a circle around globes or a */
  /* Mollewide type ellipse for that type of rectangular chart.      */

  DrawColor(gi.kiOn);

  if (!fGlobe)
  {
    if (gs.fMollewide && gi.nMode != gAstroGraph)
    {
      if (!gs.fAlt)
      {
        for (xold = 0, y = -89; y <= 90; y++, xold = x)
        {
          for (x = NMollewide(y), i = -1; i <= 1; i += 2)
          {
            DrawLine(180*nScl + i*xold - (i == 1), (90+y)*nScl, 180*nScl + i*x - (i == 1), (91+y)*nScl);
          }
        }
      }
    }
  }
  else DrawEllipse(0, 0, gs.xWin-1, gs.yWin-1);

  /* Now, if we are in an appropriate bonus chart mode, draw each planet at */
  /* its zenith or visible location on the globe or map, if not hidden.     */

  if (!gs.fAlt || (gi.nMode != gGlobe && (!fSky || gi.nMode != gWorldMap || gs.fMollewide)))
    return;

  rT = gs.fConstel ? rDegHalf - (fGlobe ? 0.0 : (real)deg) : Lon;

  if (rT < 0.0)
    rT += rDegMax;

  for (i = 1; i <= cObj; i++)
  {
    planet1[i] = RFromD(Tropical(planet[i]));
    planet2[i] = RFromD(planetalt[i]);

    EclToEqu(&planet1[i], &planet2[i]);    /* Calculate zenith long. & lat. */
  }

  /* Compute screen coordinates of each object, if it's even visible. */

  for (i = 1; i <= cObj; i++) if (FProper(i))
  {
    if (fSky)
      x1 = planet1[i];
    else
      x1 = planet1[oMC]-planet1[i];

    if (x1 < 0.0)   x1 += 2.0*rPi;
    if (x1 > rPi)   x1 -= 2.0*rPi;

    x1 = Mod(rDegHalf-rT-DFromR(x1));
    y1 = rDegQuad-DFromR(planet2[i]);

    if (fGlobe)
    {
      X[i] = FGlobeCalc(x1, y1, &u, &v, cx, cy, rx, ry, deg) ? -1000 : u;
      Y[i] = v;
    }
    else
    {
      X[i] = (int)(x1 * (real)nScl);
      Y[i] = (int)(y1 * (real)nScl);
    }
    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 <= cObj; i++) if (FProper(i))
  {
    k = l = gs.xWin+gs.yWin;

    /* 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 (FProper(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, then we'll draw the glyph above instead.   */

    if (k < unit || l < unit)
    {
      if (k < l)
        N[i] -= unit;
    }
  }
  for (i = cObj; i >= 1; i--) if (X[i] >= 0 && FProper(i))      /* Draw the */
    DrawObject(i, M[i], N[i]);                                  /* glyphs.  */

  for (i = cObj; i >= 1; i--) if (X[i] >= 0 && FProper(i))
  {
    DrawColor(kObjB[i]);
    DrawSpot(X[i], Y[i]);
  }
}


/* 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 DrawChartX()
{
  char sz[cchSzDef];
  int i;
  bool fT;

  gi.nScale = gs.nScale/100;

  if (gs.fBitmap || gs.fMeta)
    PrintNotice("Creating graphics chart in memory.");
  DrawClearScreen();

#ifdef CONSTEL
  fT = gs.fConstel;
#else
  fT = fFalse;
#endif

  switch (gi.nMode) {

  case gWheel:
  case gHouse:
    if (us.nRel > rcDual)
      XChartWheel();
    else
      XChartWheelRelation();
    break;

  case gGrid:
    if (us.nRel > rcDual)
      XChartGrid();
    else
      XChartGridRelation();
    break;

  case gHorizon:
    if (us.fPrimeVert)
      XChartHorizonSky();
    else
      XChartHorizon();
    break;

  case gOrbit:
    XChartOrbit();
    break;

  case gDisposit:
    XChartDispositor();
    break;

  case gAstroGraph:
    DrawMap(fFalse, fFalse, gs.nRot);  /* First draw map of world.           */
    XChartAstroGraph();                /* Then draw astro-graph lines on it. */
    break;

  case gCalendar:
    XChartCalendar();
    break;

  case gEphemeris:
    XChartEphemeris();
    break;

  case gWorldMap:
    DrawMap(fT, fFalse, gs.nRot);           /* First draw map of world. */
    if (!fT && gs.fAlt && !gs.fMollewide)   /* Then maybe Ley lines.    */
      DrawLeyLines(gs.nRot);
    break;

  case gGlobe:
  case gPolar:
    DrawMap(fT, fTrue, gs.nRot);
    break;

#ifdef BIORHYTHM
  case gBiorhythm:
    XChartBiorhythm();
    break;
#endif
  }

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

  DrawColor(gi.kiLite);
  if (fDrawText)
  {
    if (Mon == -1)
      sprintf(sz, "(No time or space)");

    else if (us.nRel == rcComposite)
      sprintf(sz, "(Composite)");

    else
    {
      fT = us.fAnsi; us.fAnsi = -(!gs.fFont || (!gs.fMeta && !gs.fPS));
      i = DayOfWeek(Mon, Day, Yea);

      sprintf(sz, "%c%c%c %s %s (%cT %s GMT) %s", chDay3(i),
        SzDate(Mon, Day, Yea, 2), SzTim(Tim), Dst != 0.0 ? 'D' : 'S',
        SzZone(Zon), SzLocation(Lon, Lat));
      us.fAnsi = fT;
    }
    DrawSz(sz, gs.xWin/2, gs.yWin-3*gi.nScaleT, dtBottom | dtErase);
  }

  /* Draw a border around the chart if the mode is set and appropriate. */

  if (fDrawBorder)
    DrawEdgeAll();
}
#endif /* GRAPH */

/* xcharts0.c */
