/*
** Astrolog (Version 4.40) File: charts2.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.
*/

#include "astrolog.h"


/*
******************************************************************************
** Dual Chart Display Routines.
******************************************************************************
*/

/* Print out an aspect (or midpoint if -g0 switch in effect) grid of a      */
/* relationship chart. This is similar to the ChartGrid() routine; however, */
/* here we have both axes labeled with the planets for the two charts in    */
/* question, instead of just a diagonal down the center for only one chart. */

void ChartGridRelation()
{
  char sz[cchSzDef];
  int i, j, k, tot = cObj, temp;

#ifdef INTERPRET
  if (us.fInterpret && !us.fGridConfig) {
    InterpretGridRelation();
    return;
  }
#endif
  PrintSz(" 2>");
  for (temp = 0, i = 1; i <= cObj; i++) if (!ignore[i]) {
    PrintCh(chV);
    AnsiColor(kObjA[i]);
    sprintf(sz, "%c%c%c", chObj3(i)); PrintSz(sz);
    AnsiColor(kDefault);
    temp++;
  }
  PrintSz("\n1  ");
  for (i = 1; i <= tot; i++) if (!ignore[i]) {
    PrintCh(chV);
    AnsiColor(kSignA(SFromZ(cp2.obj[i])));
    sprintf(sz, "%2d%c", (int)cp2.obj[i] % 30, chDeg0); PrintSz(sz);
    AnsiColor(kDefault);
  }
  PrintSz("\nV  ");
  for (i = 1; i <= tot; i++) if (!ignore[i]) {
    PrintCh(chV);
    temp = SFromZ(cp2.obj[i]);
    AnsiColor(kSignA(temp));
    sprintf(sz, "%c%c%c", chSig3(temp)); PrintSz(sz);
    AnsiColor(kDefault);
  }
  PrintL();
  for (j = 1; j <= cObj; j++) if (!ignore[j])
    for (k = 1; k <= 4; k++) {
      if (k < 2)
        PrintTab(chH, 3);
      else if (k == 2) {
        AnsiColor(kObjA[j]);
        sprintf(sz, "%c%c%c", chObj3(j)); PrintSz(sz);
      } else {
        temp = SFromZ(cp1.obj[j]);
        AnsiColor(kSignA(temp));
        if (k == 3)
          sprintf(sz, "%2d%c", (int)cp1.obj[j] - (temp-1)*30, chDeg0);
        else
          sprintf(sz, "%c%c%c", chSig3(temp));
        PrintSz(sz);
      }
      if (k > 1)
        AnsiColor(kDefault);
      for (i = 1; i <= tot; i++) if (!ignore[i]) {
        PrintCh((char)(k < 2 ? chC : chV));
        temp = grid->n[i][j];
        if (k > 1) {
          if (i == j)
            AnsiColor(kReverse);
          AnsiColor(us.fGridConfig ? kSignA(temp) :
            kAspA[temp]);
        }
        if (k < 2)
          PrintTab(chH, 3);
        else if (k == 2) {
          if (us.fGridConfig)
            sprintf(sz, "%c%c%c", chSig3(temp));
          else
            sprintf(sz, "%s", temp ? szAspectAbbrev[temp] : "   ");
          PrintSz(sz);
        } else if (k == 3) {
          if (us.fGridConfig) {
            sprintf(sz, "%2d%c", grid->v[i][j]/60, chDeg0); PrintSz(sz);
          } else
            if (grid->n[i][j]) {
              if (grid->v[i][j] < 6000)
                sprintf(sz, "%c%2d", us.fAppSep ?
                  (grid->v[i][j] < 0 ? 'a' : 's') :
                  (grid->v[i][j] < 0 ? '-' : '+'), abs(grid->v[i][j])/60);
              else
                sprintf(sz, "%3d", abs(temp)/60);
              PrintSz(sz);
            } else
              PrintSz("   ");
        } else {
          if (grid->n[i][j]) {
            sprintf(sz, "%02d'", abs(grid->v[i][j])%60); PrintSz(sz);
          } else
            PrintSz("   ");
        }
        AnsiColor(kDefault);
      }
      PrintL();
    }
}


/* Display all aspects between objects in the relationship comparison chart, */
/* one per line, in sorted order based on the total "power" of the aspects,  */
/* as specified with the -r0 -a switch combination.                          */

void ChartAspectRelation()
{
  char sz[cchSzDef];
  int pcut = 30000, icut, jcut, phi, ihi, jhi, ahi, p, i, j, k, count = 0;
  real ip, jp;

  loop {
    phi = -1;

    /* Search for the next most powerful aspect in the aspect grid. */

    for (i = 1; i <= cObj; i++) if (!ignore[i])
      for (j = 1; j <= cObj; j++) if (!ignore[j])
        if (k = grid->n[i][j]) {
          ip = i <= oNorm ? objectinf[i] : 2.5;
          jp = j <= oNorm ? objectinf[j] : 2.5;
          p = (int)(aspectinf[k]*(ip+jp)/2.0*
            (1.0-RAbs((real)(grid->v[i][j]))/60.0/aspectorb[k])*1000.0);
          if ((p < pcut || (p == pcut && (i > icut ||
            (i == icut && j > jcut)))) && p > phi) {
            ihi = i; jhi = j; phi = p; ahi = k;
          }
        }
    if (phi < 0)    /* Exit when no less powerful aspect found. */
      break;
    pcut = phi; icut = ihi; jcut = jhi;
    count++;                              /* Display the current aspect.   */
#ifdef INTERPRET
    if (us.fInterpret) {                  /* Interpret it if -I in effect. */
      InterpretAspectRelation(jhi, ihi);
      continue;
    }
#endif
    sprintf(sz, "%3d: ", count); PrintSz(sz);
    PrintAspect(jhi, SFromZ(cp1.obj[jhi]), (int)RSgn(cp1.dir[jhi]), ahi,
      ihi, SFromZ(cp2.obj[ihi]), (int)RSgn(cp2.dir[ihi]), 'A');
    k = grid->v[ihi][jhi];
    AnsiColor(k < 0 ? kWhite : kLtGray);
    sprintf(sz, "- orb: %c%d,%02d'",
      us.fAppSep ? (k < 0 ? 'a' : 's') : (k < 0 ? '-' : '+'),
      abs(k)/60, abs(k)%60); PrintSz(sz);
    AnsiColor(kDkGreen);
    sprintf(sz, " - power:%6.2f\n", (real)phi/1000.0); PrintSz(sz);
    AnsiColor(kDefault);
  }
}


/* Display locations of all midpoints between objects in the relationship */
/* comparison chart, one per line, in sorted zodiac order from zero Aries */
/* onward, as specified with the -r0 -m switch combination.               */

void ChartMidpointRelation()
{
  char sz[cchSzDef];
  int mcut = -1, icut, jcut, mlo, ilo, jlo, m, i, j, count = 0;

  loop {
    mlo = 21600;

    /* Search for the next closest midpoint farther down in the zodiac. */ 

    for (i = 1; i <= cObj; i++) if (!ignore[i])
      for (j = 1; j <= cObj; j++) if (!ignore[j]) {
        m = (grid->n[j][i]-1)*30*60 + grid->v[j][i];
        if ((m > mcut || (m == mcut && (i > icut ||
          (i == icut && j > jcut)))) && m < mlo) {
          ilo = i; jlo = j; mlo = m;
        }
      }
    if (mlo >= 21600)    /* Exit when no midpoint farther in zodiac found. */
      break;
    mcut = mlo; icut = ilo; jcut = jlo;
    count++;                               /* Display the current midpoint. */
#ifdef INTERPRET
    if (us.fInterpret) {                   /* Interpret it if -I in effect. */
      InterpretMidpointRelation(ilo, jlo);
      continue;
    }
#endif
    sprintf(sz, "%4d: ", count); PrintSz(sz);
    PrintZodiac((real)mlo/60.0);
    PrintCh(' ');
    PrintAspect(ilo, SFromZ(cp1.obj[ilo]), (int)RSgn(cp1.dir[ilo]), 0,
      jlo, SFromZ(cp2.obj[jlo]), (int)RSgn(cp2.dir[jlo]), 'M');
    AnsiColor(kDefault);
    m = (int)(MinDistance(cp1.obj[ilo], cp2.obj[jlo])*60.0);
    sprintf(sz, "-%4d%c%02d' degree span.\n", m/60, chDeg1, m%60);
    PrintSz(sz);
  }
}


/* Calculate any of the various kinds of relationship charts. This involves */
/* reading in and storing the planet and house positions for both charts,   */
/* and then combining them in the main single chart in the proper manner.   */
/* If the parameter 'fFile' is on, then we read the info for the two charts */
/* from files, otherwise use the info in preset "core" and "second" charts. */

void CastRelation(fFile)
bool fFile;
{
  byte ignoreT[objMax];
  CI ciT;
  int i;
  real ratio, t1, t2, t;

  /* Read in and cast the first chart. */

  if (fFile)
    FInputData(is.szFile);
  ciT = ciCore;
  if (fFile)
    ciTwin = ciCore;
  else
    ciCore = ciTwin;
  t1 = CastChart(fTrue);
  for (i = 1; i <= cSign; i++) {
    cp1.cusp[i] = house[i];
    cp1.house[i] = inhouse[i];
  }
  for (i = 1; i <= cObj; i++) {
    cp1.obj[i] = planet[i];
    cp1.alt[i] = planetalt[i];
    cp1.dir[i] = ret[i];
  }

  /* Read in the second chart. */

  if (fFile) {
    FInputData(is.szFile2);
    if (us.nRel == rcProgress) {
      us.fProgress = fTrue;
      is.JDp = MdytszToJulian(MM, DD, YY, TT, SS, ZZ);
      ciCore = ciT;
    }
  } else
    ciCore = ciT;
  ciMain = ciCore;
  if (us.nRel == rcTransit)
    for (i = 1; i <= cObj; i++) {
      ignoreT[i] = ignore[i];
      ignore[i] = ignore[i] && ignore2[i];
    }
  t2 = CastChart(fTrue);
  if (us.nRel == rcTransit)
    for (i = 1; i <= cObj; i++)
      ignore[i] = ignoreT[i];
  for (i = 1; i <= cSign; i++) {
    cp2.cusp[i] = house[i];
    cp2.house[i] = inhouse[i];
  }
  for (i = 1; i <= cObj; i++) {
    cp2.obj[i] = planet[i];
    cp2.alt[i] = planetalt[i];
    cp2.dir[i] = ret[i];
  }

  /* Now combine the two charts based on what relation we are doing.   */
  /* For the standard -r synastry chart, use the house cusps of chart1 */
  /* and the planets positions of chart2.                              */

  ratio = (real)us.nRatio1 / ((real)(us.nRatio1 + us.nRatio2));
  if (us.nRel <= rcSynastry)
    for (i = 1; i <= cSign; i++)
      house[i] = cp1.cusp[i];

  /* For the -rc composite chart, take the midpoints of the planets/houses. */

  else if (us.nRel == rcComposite) {
    for (i = 1; i <= cObj; i++) {
      planet[i] = Ratio(cp1.obj[i], cp2.obj[i], ratio);
      if (RAbs(cp2.obj[i] - cp1.obj[i]) > rDegHalf)
        planet[i] = Mod(planet[i] + rDegMax*ratio);
      planetalt[i] = Ratio(cp1.alt[i], cp2.alt[i], ratio);
      ret[i] = Ratio(cp1.dir[i], cp2.dir[i], ratio);
    }
    for (i = 1; i <= cSign; i++) {
      house[i] = Ratio(cp1.cusp[i], cp2.cusp[i], ratio);
      if (RAbs(cp2.cusp[i] - cp1.cusp[i]) > rDegHalf)
        house[i] = Mod(house[i] + rDegMax*ratio);
    }

    /* Make sure we don't have any 180 degree errors in house cusp    */
    /* complement pairs, which may happen if the cusps are far apart. */

    for (i = 1; i <= cSign; i++)
      if (MinDistance(house[sCap], Mod(house[i]-ZFromS(i+3))) > rDegQuad)
        house[i] = Mod(house[i]+rDegHalf);
    for (i = 1; i <= cSign; i++)
      if (RAbs(MinDistance(house[i], planet[oAsc - 1 + i])) > rDegQuad)
        planet[oAsc - 1 + i] = Mod(planet[oAsc - 1 + i]+rDegHalf);

  /* For the -rm time space midpoint chart, calculate the midpoint time and */
  /* place between the two charts and then recast for the new chart info.   */

  } else if (us.nRel == rcMidpoint) {
    T = Ratio(t1, t2, ratio);
    t = (T*36525.0)+rRound; is.JD = RFloor(t)+2415020.0; TT = RFract(t)*24.0;
    ZZ = Ratio(DecToDeg(ciT.zon), DecToDeg(Zon), ratio);
    SS = Ratio(DecToDeg(ciT.dst), DecToDeg(Dst), ratio);
    TT -= ZZ;
    if (TT < 0.0) {
      TT += 24.0; is.JD -= 1.0;
    }
    JulianToMdy(is.JD, &MM, &DD, &YY);
    OO = Ratio(DecToDeg(ciT.lon), DecToDeg(Lon), ratio);
    if (RAbs(Lon-ciT.lon) > rDegHalf)
      OO = Mod(OO+rDegMax*ratio);
    AA = Ratio(DecToDeg(ciT.lat), DecToDeg(Lat), ratio);
    TT = DegToDec(TT); SS = DegToDec(SS); ZZ = DegToDec(ZZ);
    OO = DegToDec(OO); AA = DegToDec(AA);
    ciMain = ciCore;
    CastChart(fTrue);

  /* There are a couple of non-astrological charts, which only require the */
  /* number of days that have passed between the two charts to be done.    */

  } else
    is.JD = RAbs(t2-t1)*36525.0;
  ComputeInHouses();
}


/*
******************************************************************************
** Other Chart Display Routines.
******************************************************************************
*/

/* Given two objects and an aspect between them, or an object and a sign  */
/* that it's entering, print if this is a "major" event, such as a season */
/* change or major lunar phase. This is called from the ChartInDay()      */
/* searching and influence routines. Do an interpretation if need be too. */

void PrintInDay(source, aspect, dest)
int source, aspect, dest;
{
  if (aspect == aSig) {
    if (source == oSun) {
      AnsiColor(kWhite);
      if (dest == 1)
        PrintSz(" (Vernal Equinox)");     /* If the Sun changes sign, */
      else if (dest == 4)                 /* then print out if this   */
        PrintSz(" (Summer Solstice)");    /* is a season change.      */
      else if (dest == 7)
        PrintSz(" (Autumnal Equinox)");
      else if (dest == 10)
        PrintSz(" (Winter Solstice)");
    }
  } else if (aspect > 0) {
    if (source == oSun && dest == oMoo) {
      if (aspect <= aSqu)
        AnsiColor(kWhite);
      if (aspect == aCon)
        PrintSz(" (New Moon)");     /* Print out if the present */
      else if (aspect == aOpp)      /* aspect is a New, Full,   */
        PrintSz(" (Full Moon)");    /* or Half Moon.            */
      else if (aspect == aSqu)
        PrintSz(" (Half Moon)");
    }
  }
  PrintL();

#ifdef INTERPRET
  if (us.fInterpret)
    InterpretInDay(source, aspect, dest);
#endif
  AnsiColor(kDefault);
}


/* Given two objects and an aspect (or one object, and an event such as a */
/* sign or direction change) display the configuration in question. This  */
/* is called by the many charts which list aspects among items, such as   */
/* the -a aspect lists, -m midpoint lists, -d aspect in day search and    */
/* influence charts, and -t transit search and influence charts.          */

void PrintAspect(obj1, sign1, ret1, asp, obj2, sign2, ret2, chart)
int obj1, sign1, ret1, asp, obj2, sign2, ret2;
char chart;
{
  char sz[cchSzDef];

  AnsiColor(kObjA[obj1]);
  if (chart == 't' || chart == 'T')
    PrintSz("trans ");
  else if (chart == 'e' || chart == 'u' || chart == 'U')
    PrintSz("progr ");
  sprintf(sz, "%7.7s", szObjName[obj1]); PrintSz(sz);
  AnsiColor(kSignA(sign1));
  sprintf(sz, " %c%c%c%c%c",
    ret1 > 0 ? '(' : (ret1 < 0 ? '[' : '<'), chSig3(sign1),
    ret1 > 0 ? ')' : (ret1 < 0 ? ']' : '>')); PrintSz(sz);
  AnsiColor(asp > 0 ? kAspA[asp] : kWhite);
  PrintCh(' ');
  if (asp == aSig)
    sprintf(sz, "-->");                        /* Print a sign change. */
  else if (asp == aDir)
    sprintf(sz, "S/%c", obj2 ? chRet : 'D');   /* Print a direction change. */
  else if (asp == 0)
    sprintf(sz, chart == 'm' ? "&" : "with");
  else
    sprintf(sz, "%s", szAspectAbbrev[asp]);    /* Print an aspect. */
  PrintSz(sz);
  if (asp != aDir)
    PrintCh(' ');
  if (chart == 'A')
    PrintSz("with ");
  if (asp == aSig) {
    AnsiColor(kSignA(obj2));
    sprintf(sz, "%s", szSignName[obj2]); PrintSz(sz);
  } else if (asp >= 0) {
    AnsiColor(kSignA(sign2));
    if (chart == 't' || chart == 'u' || chart == 'T' || chart == 'U')
      PrintSz("natal ");
    sprintf(sz, "%c%c%c%c%c ",
      ret2 > 0 ? '(' : (ret2 < 0 ? '[' : '<'), chSig3(sign2),
      ret2 > 0 ? ')' : (ret2 < 0 ? ']' : '>')); PrintSz(sz);
    AnsiColor(kObjA[obj2]);
    sprintf(sz, "%.10s", szObjName[obj2]); PrintSz(sz);
  }
  if (chart == 'D' || chart == 'T' || chart == 'U' ||
    chart == 'a' || chart == 'A' || chart == 'm' || chart == 'M')
    PrintTab(' ', 10-CchSz(szObjName[obj2]));
}


/* Based on the given chart information, display all the aspects taking   */
/* place in the chart, as specified with the -D switch. The aspects are   */
/* printed in order of influence determined by treating them as happening */
/* outside among transiting planets, such that rare outer planet aspects  */
/* are given more power than common ones among inner planets. (This is    */
/* almost identical to the -a list, except the influences are different.) */

void ChartInDayInfluence()
{
  int source[MAXINDAY], aspect[MAXINDAY], dest[MAXINDAY];
  real power[MAXINDAY];
  char sz[cchSzDef];
  int occurcount = 0, i, j, k, l, m;

  /* Go compute the aspects in the chart. */

  i = us.fAppSep;
  us.fAppSep = fTrue;     /* We always want applying vs. separating orbs. */
  FCreateGrid(fFalse);
  us.fAppSep = i;

  /* Search through the grid and build up the list of aspects. */

  for (j = 2; j <= cObj; j++) {
    if (ignore[j])
      continue;
    for (i = 1; i < j; i++) {
      if (ignore[i] || (k = grid->n[i][j]) == 0 || occurcount >= MAXINDAY)
        continue;
      source[occurcount] = i; aspect[occurcount] = k; dest[occurcount] = j;
      l = grid->v[i][j];
      power[occurcount] =
        ((i <= oNorm ? transitinf[i] : 2.0)/4.0)*
        ((j <= oNorm ? transitinf[j] : 2.0)/4.0)*
        aspectinf[k]*(1.0-(real)abs(l)/60.0/GetOrb(i, j, k));
      occurcount++;
    }
  }

  /* Sort aspects by order of influence. */

  for (i = 1; i < occurcount; i++) {
    j = i-1;
    while (j >= 0 && power[j] < power[j+1]) {
      SwapN(source[j], source[j+1]);
      SwapN(aspect[j], aspect[j+1]);
      SwapN(dest[j], dest[j+1]);
      SwapR(&power[j], &power[j+1]);
      j--;
    }
  }

  /* Now display each aspect line. */

  for (i = 0; i < occurcount; i++) {
    sprintf(sz, "%3d: ", i+1); PrintSz(sz);
    j = source[i]; k = aspect[i]; l = dest[i];
    PrintAspect(
      j, SFromZ(planet[j]), (int)RSgn(ret[j]), k,
      l, SFromZ(planet[l]), (int)RSgn(ret[l]), 'D');
    m = grid->v[j][l];
    AnsiColor(m < 0 ? kWhite : kLtGray);
    sprintf(sz, "- %s%2d%c%02d'", m < 0 ? "app" : "sep",
      abs(m)/60, chDeg1, abs(m)%60); PrintSz(sz);
    AnsiColor(kDkGreen);
    sprintf(sz, " - power:%6.2f", power[i]); PrintSz(sz);
    PrintInDay(j, k, l);
  }
}


/* Given an arbitrary day, determine what aspects are made between this */
/* transiting chart and the given natal chart, as specified with the -T */
/* switch, and display the transits in order sorted by influence.       */

void ChartTransitInfluence(fProg)
bool fProg;
{
  int source[MAXINDAY], aspect[MAXINDAY], dest[MAXINDAY];
  real power[MAXINDAY];
  byte ignore3[objMax];
  char sz[cchSzDef];
  int occurcount = 0, i, j, k, l, m;

  /* Cast the natal and transiting charts as with a relationship chart. */

  for (i = 1; i <= cSign; i++)
    cp1.cusp[i] = house[i];
  for (i = 1; i <= cObj; i++) {
    cp1.obj[i] = planet[i];
    cp1.dir[i] = ret[i];
  }
  ciCore = ciTwin;
  if (us.fProgress = fProg) {
    is.JDp = MdytszToJulian(MM, DD, YY, TT, SS, ZZ);
    ciCore = ciMain;
  }
  CastChart(fTrue);
  for (i = 1; i <= cSign; i++)
    cp2.cusp[i] = house[i];
  for (i = 1; i <= cObj; i++) {
    cp2.obj[i] = planet[i];
    cp2.dir[i] = ret[i];
  }

  /* Do a relationship aspect grid to get the transits. We have to make and */
  /* restore three changes to get it right for this chart. (1) We make the  */
  /* natal planets have zero velocity so applying vs. separating is only a  */
  /* function of the transiter. (2) We force applying vs. separating orbs   */
  /* regardless if -ga or -ma is in effect or not. (3) Finally we tweak the */
  /* main restrictions to allow for transiting objects not restricted.      */

  for (i = 1; i <= cObj; i++) {
    ret[i] = cp1.dir[i];
    cp1.dir[i] = 0.0;
    ignore3[i] = ignore[i];
    ignore[i] = ignore[i] && ignore2[i];
  }
  i = us.fAppSep;
  us.fAppSep = fTrue;
  FCreateGridRelation(fFalse);
  us.fAppSep = i;
  for (i = 1; i <= cObj; i++) {
    cp1.dir[i] = ret[i];
    ignore[i] = ignore3[i];
  }

  /* Loop through the grid, and build up a list of the valid transits. */

  for (i = 1; i <= oNorm; i++) {
    if (ignore2[i] || !FThing(i))
      continue;
    for (j = 1; j <= cObj; j++) {
      if (ignore[j] || (k = grid->n[i][j]) == 0 || occurcount >= MAXINDAY)
        continue;
      source[occurcount] = i; aspect[occurcount] = k; dest[occurcount] = j;
      l = grid->v[i][j];
      power[occurcount] = transitinf[i]*
        ((j <= oNorm ? objectinf[j] : 2.0)/4.0)*aspectinf[k]*
        (1.0-(real)abs(l)/60.0/GetOrb(i, j, k));
      occurcount++;
    }
  }

  /* After all transits located, sort them by their total power. */

  for (i = 1; i < occurcount; i++) {
    j = i-1;
    while (j >= 0 && power[j] < power[j+1]) {
      SwapN(source[j], source[j+1]);
      SwapN(aspect[j], aspect[j+1]);
      SwapN(dest[j], dest[j+1]);
      SwapR(&power[j], &power[j+1]);
      j--;
    }
  }

  /* Now loop through list and display each transit in effect at the time. */

  for (i = 0; i < occurcount; i++) {
    k = aspect[i];
    l = source[i];
    sprintf(sz, "%3d: ", i+1); PrintSz(sz);
    j = SFromZ(cp2.obj[l]);
    PrintAspect(l, j, (int)RSgn(cp2.dir[l]), k,
      dest[i], SFromZ(cp1.obj[dest[i]]), (int)RSgn(cp1.dir[dest[i]]),
      (char)(fProg ? 'U' : 'T'));
    m = grid->v[l][dest[i]];
    AnsiColor(m < 0 ? kWhite : kLtGray);
    sprintf(sz, "- %s%2d%c%02d'", m < 0 ? "app" : "sep",
      abs(m)/60, chDeg1, abs(m)%60); PrintSz(sz);
    AnsiColor(kDkGreen);
    sprintf(sz, " - power:%6.2f", power[i]); PrintSz(sz);
    if (k == aCon && l == dest[i]) {    /* Print a small "R" for returns. */
      AnsiColor(kWhite);
      PrintSz(" R");
    }
    PrintL();
#ifdef INTERPRET
    if (us.fInterpret)
      InterpretTransit(l, k, dest[i]);
#endif
    AnsiColor(kDefault);
  }
}


/* Given the zodiac location of a planet in the sky and its declination,   */
/* and a location on the Earth, compute the azimuth and altitude of where  */
/* on the local horizon sky the planet would appear to one at the given    */
/* location. A reference MC position at Greenwich is also needed for this. */

void EclToHorizon(azi, alt, planet, planetalt, lon, lat, mc)
real *azi, *alt, planet, planetalt, lon, lat, mc;
{
  real lonz, latz;

  lonz = RFromD(planet); latz = RFromD(planetalt);
  EclToEqu(&lonz, &latz);
  lonz = RFromD(Mod(DFromR(mc-lonz+lon)));
  lonz = RFromD(Mod(DFromR(lonz-lon+rPiHalf)));
  EquToLocal(&lonz, &latz, rPiHalf-lat);
  *azi = rDegMax-DFromR(lonz); *alt = DFromR(latz);
}


/* Display a calendar for the given month in the chart, as specified with  */
/* with the -K switch. When color is on, the title is white, weekends are  */
/* highlighted in red, and the specific day in the chart is colored green. */

void ChartCalendarMonth()
{
  char sz[cchSzDef];
  int i, j, k;

  AnsiColor(kWhite);
  PrintTab(' ', 16-CchSz(szMonth[Mon]) >> 1);
  sprintf(sz, "%s%5d\n", szMonth[Mon], Yea); PrintSz(sz);
  for (i = 0; i < 7; i++) {
    sprintf(sz, "%c%c%c", szDay[i][0], szDay[i][1], i < 6 ? ' ' : '\n');
    PrintSz(sz);
  }
  j = DayOfWeek(Mon, 1, Yea);
  AnsiColor(kDefault);
  for (i = 0; i < j; i++) {
    if (i == 0)
      AnsiColor(kRainbowA[1]);
    PrintSz("-- ");
    if (i == 0)
      AnsiColor(kDefault);
  }
  k = DayInMonth(Mon, Yea);
  for (i = 1; i <= k; i = AddDay(Mon, i, Yea, 1)) {
    if (i == (int)Day)
      AnsiColor(kRainbowA[4]);
    else if (j == 0 || j == 6)
      AnsiColor(kRainbowA[1]);
    sprintf(sz, "%2d", i); PrintSz(sz);
    if (j == 0 || j == 6 || i == Day)
      AnsiColor(kDefault);
    if (j < 6) {
      j++;
      PrintCh(' ');
    } else {
      j = 0;
      PrintL();
    }
  }
  while (j > 0 && j < 7) {
    if (j == 6)
      AnsiColor(kRainbowA[1]);
    j++;
    sprintf(sz, "--%c", j < 7 ? ' ' : '\n'); PrintSz(sz);
  }
  AnsiColor(kDefault);
}


/* Display a calendar for the entire year given in the chart, as specified */
/* with the -Ky switch. This is just like twelve of the individual month   */
/* calendars above displayed together, with same color highlights and all. */

void ChartCalendarYear()
{
  char sz[cchSzDef];
  int r, w, c, m, d, dy, p[3], l[3], n[3];

  dy = DayOfWeek(1, 1, Yea);
  for (r = 0; r < 4; r++) {     /* Loop over one set of three months */
    AnsiColor(kWhite);
    for (c = 0; c < 3; c++) {
      m = r*3+c+1;
      PrintTab(' ', 16-CchSz(szMonth[m]) >> 1);
      sprintf(sz, "%s%5d", szMonth[m], Yea); PrintSz(sz);
      if (c < 2)
        PrintTab(' ', 20 + MONTHSPACE -
          (16-CchSz(szMonth[m]) >> 1) - CchSz(szMonth[m]) - 5);
    }
    PrintL();
    for (c = 0; c < 3; c++) {
      for (d = 0; d < 7; d++) {
        sprintf(sz, "%c%c%c", szDay[d][0], szDay[d][1],
          d < 6 || c < 2 ? ' ' : '\n'); PrintSz(sz);
      }
      if (c < 2)
        PrintTab(' ', MONTHSPACE-1);
      m = r*3+c+1;
      p[c] = dy % 7;
      l[c] = DayInMonth(m, Yea);
      n[c] = 0;
      dy += DaysInMonth(m, Yea);
    }
    for (w = 0; w < 6; w++) {      /* Loop over one set of week rows */
      for (c = 0; c < 3; c++) {    /* Loop over one week in a month  */
        m = r*3+c+1;
        d = 0;
        if (w == 0)
          while (d < p[c]) {
            if (d == 0)
              AnsiColor(kRainbowA[1]);
            PrintSz("-- ");
            if (d == 0)
              AnsiColor(kDefault);
            d++;
          }
        AnsiColor(kDefault);
        while (d < 7 && n[c] < l[c]) {
          n[c] = AddDay(m, n[c], Yea, 1);
          if (n[c] == Day && m == Mon)
            AnsiColor(kRainbowA[4]);
          else if (d == 0 || d == 6)
            AnsiColor(kRainbowA[1]);
          sprintf(sz, "%2d%c", n[c], d < 6 || c < 2 ? ' ' : '\n');
          PrintSz(sz);
          if (d == 0 || d == 6 || (n[c] == Day && m == Mon))
            AnsiColor(kDefault);
          d++;
        }
        while (d < 7) {
          if (d == 0 || d == 6)
            AnsiColor(kRainbowA[1]);
          sprintf(sz, "--%c", d < 6 || c < 2 ? ' ' : '\n'); PrintSz(sz);
          if (d == 0)
            AnsiColor(kDefault);
          d++;
        }
        if (c < 2)
          PrintTab(' ', MONTHSPACE-1);
      }
    }
    if (r < 3)
      PrintL();
  }
  AnsiColor(kDefault);
}


/* Display either a biorhythm chart or the time difference in various units */
/* between two charts, i.e. two types of relationship "charts" that aren't  */
/* related in any way to planetary positions, as specified by either the    */
/* -rb or -rd switches, respectively.                                       */

void DisplayRelation()
{
  char sz[cchSzDef];
  int i;
#ifdef BIORHYTHM
  int j;
  real k, l;
#endif

  /* If we are calculating the difference between two dates, then display */
  /* the value and return, as with the -rd switch.                        */

  if (us.nRel == rcDifference) {
    PrintSz("Differences between the dates in the two charts:\n");
    for (i = 1; i <= 7; i++) {
      AnsiColor(kRainbowA[i]);
      switch (i) {
      case 1: sprintf(sz, "Years  : %.0f", is.JD/365.25);      break;
      case 2: sprintf(sz, "Months : %.0f", is.JD/(365.25/12)); break;
      case 3: sprintf(sz, "Weeks  : %.0f", is.JD/7.0);         break;
      case 4: sprintf(sz, "Days   : %.0f", is.JD);             break;
      case 5: sprintf(sz, "Hours  : %.0f", is.JD*24.0);        break;
      case 6: sprintf(sz, "Minutes: %.0f", is.JD*24.0*60.0);   break;
      case 7: sprintf(sz, "Seconds: %.0f", is.JD*24.0*3600.0); break;
      }
      PrintSz(sz);
      PrintL();
    }
    AnsiColor(kDefault);
    return;
  }

#ifdef BIORHYTHM
  /* If we are doing a biorhythm (-rb switch), then we'll calculate it for */
  /* someone born on the older date, at the time of the younger date. Loop */
  /* through the week preceeding and following the date in question.       */

  is.JD = RFloor(is.JD + rRound);
  for (is.JD -= 7.0, i = -7; i <= 7; i++, is.JD += 1.0) {
    if (i == 0)
      AnsiColor(kWhite);
    else if (i == 1)
      AnsiColor(kDefault);
    sprintf(sz, "T%c%d Day%c:",
      i < 0 ? '-' : '+', abs(i), abs(i) != 1 ? 's' : ' '); PrintSz(sz);
    for (j = 1; j <= 3; j++) {
      PrintCh(' ');
      switch (j) {
      case 1: k = brPhy; AnsiColor(kRed);   PrintSz("Physical");     break;
      case 2: k = brEmo; AnsiColor(kBlue);  PrintSz("Emotional");    break;
      case 3: k = brInt; AnsiColor(kGreen); PrintSz("Intellectual"); break;
      }
      AnsiColor(i ? kDefault : kWhite);

      /* The biorhythm calculation is below. */

      l = RBiorhythm(is.JD, k);
      sprintf(sz, " at %c%3.0f%%", l < 0.0 ? '-' : '+', RAbs(l)); PrintSz(sz);

      /* Print smiley face, medium face, or sad face based on current cycle. */

      AnsiColor(kPurple);
      sprintf(sz, " :%c", l > 50.0 ? ')' : (l < -50.0 ? '(' : '|'));
      PrintSz(sz);
      AnsiColor(i ? kDefault : kWhite);
      if (j < 3)
        PrintCh(',');
    }
    PrintL();
  }
#endif /* BIORHYTHM */
}

/* charts2.c */
