home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1995 #5 & #6 / Amiga Plus CD - 1995 - No. 5 and 6.iso / pd / daten / astrolog / src / xcharts2.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-08-11  |  19.7 KB  |  576 lines

  1. /*                                                               -*- C -*-
  2. ** Astrolog (Version 4.40) File: xcharts2.c
  3. **
  4. ** IMPORTANT NOTICE: The graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen
  6. ** (astara@u.washington.edu). Permission is granted to freely use and
  7. ** distribute these routines provided one doesn't sell, restrict, or
  8. ** profit from them in any way. Modification is allowed provided these
  9. ** notices remain with any altered or edited versions of the program.
  10. **
  11. ** The main planetary calculation routines used in this program have
  12. ** been Copyrighted and the core of this program is basically a
  13. ** conversion to C of the routines created by James Neely as listed in
  14. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  15. ** available from Matrix Software. The copyright gives us permission to
  16. ** use the routines for personal use but not to sell them or profit from
  17. ** them in any way.
  18. **
  19. ** The PostScript code within the core graphics routines are programmed
  20. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  21. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  22. **
  23. ** The extended accurate ephemeris databases and formulas are from the
  24. ** calculation routines in the program "Placalc" and are programmed and
  25. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  26. ** (alois@azur.ch). The use of that source code is subject to
  27. ** regulations made by Astrodienst Zurich, and the code is not in the
  28. ** public domain. This copyright notice must not be changed or removed
  29. ** by any user of this program.
  30. **
  31. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  32. ** X Window graphics initially programmed 10/23-29/1991.
  33. ** PostScript graphics initially programmed 11/29-30/1992.
  34. ** Last code change made 1/29/1995.
  35. */
  36.  
  37. /* $VER: $Id: xcharts2.c,v 1.2 1995/07/02 22:22:38 tf Exp $ */
  38.  
  39. #include "astrolog.h"
  40.  
  41.  
  42. #ifdef GRAPH
  43. /*
  44. ******************************************************************************
  45. ** Chart Graphics Utility Procedures.
  46. ******************************************************************************
  47. */
  48.  
  49. /* Return whether the specified object should be displayed in the current */
  50. /* graphics chart type. For example, don't include the Moon in the solar  */
  51. /* system charts, don't include house cusps in astro-graph, and so on.    */
  52.  
  53. bool FProper(i)
  54. int i;
  55. {
  56.   bool f;
  57.  
  58.   if (gi.nMode == gHorizon   ||
  59.       gi.nMode == gEphemeris || fMap ||
  60.       gi.nMode == gGlobe     ||
  61.       gi.nMode == gPolar)                     /* Astro-graph / ephem charts */
  62.     f = FObject(i);
  63.  
  64.   else if (gi.nMode == gOrbit)                /* Solar system charts */
  65.     f = FObject(i) && (i != oMoo || (us.fPlacalc && us.objCenter < oMoo));
  66.  
  67.   else
  68.     f = fTrue;
  69.  
  70.   return f && !ignore[i];                     /* Check restriction status */
  71. }
  72.  
  73.  
  74. /* Adjust an array of zodiac positions so that no two positions are within   */
  75. /* a certain orb of each other. This is used by the wheel drawing chart      */
  76. /* routines in order to make sure that we don't draw any planet glyphs on    */
  77. /* top of each other. We'll later draw the glyphs at the adjusted positions. */
  78.  
  79. void FillSymbolRing(symbol)
  80. real *symbol;
  81. {
  82.   real orb = DEFORB * 256.0 / (real)gs.yWin * (real)gi.nScale;
  83.  
  84.   real k1, k2, temp;
  85.   int i, j, k = 1, l;
  86.  
  87.   /* Keep adjusting as long as we can still make changes, or until we do 'n' */
  88.   /* rounds. (With many objects, there just may not be enough room for all.) */
  89.  
  90.   for (l = 0; k && l < us.nDivision*2; l++)
  91.   {
  92.     k = 0;  /* becomes != 0 if we made changes */
  93.  
  94.     for (i = 1; i <= cObj; i++)   if (FProper(i))
  95.     {
  96.       /* For each object, determine who is closest on either side. */
  97.  
  98.       k1 =  rLarge;
  99.       k2 = -rLarge;
  100.  
  101.       for (j = 1; j <= cObj; j++)  if (FProper(j) && i != j)
  102.       {
  103.         temp = symbol[j] - symbol[i];  /* Gradabstand */
  104.  
  105.         if (RAbs(temp) > rDegHalf)
  106.           temp -= rDegMax * RSgn(temp);
  107.  
  108.         if (temp < k1 && temp >= 0.0)
  109.           k1 = temp;
  110.  
  111.         else if (temp > k2 && temp <= 0.0)
  112.           k2 = temp;
  113.       }
  114.  
  115.       /* If an object's too close on one side, then we move to the other. */
  116.  
  117.       if (k2 > -orb && k1 > orb) /* collision from the left */
  118.       {
  119.         k = 1; symbol[i] = Mod( symbol[i] + orb*0.51 + k2*0.49 );
  120.       }
  121.       else if (k1 < orb && k2 < -orb) /* collision from the right */
  122.       {
  123.         k = 1; symbol[i] = Mod( symbol[i] - orb*0.51 + k1*0.49 );
  124.  
  125.       /* If we are bracketed by close objects on both sides, then let's move */
  126.       /* to the midpoint, so we are as far away as possible from either one. */
  127.  
  128.       }
  129.       else if (k2 > -orb && k1 < orb)
  130.       {
  131.         /* Problem: 3 symbols at an equal position will not be moved! */
  132.         k = 1; symbol[i] = Mod( symbol[i] + (k1+k2) * 0.5 );
  133.       }
  134.     }
  135.   }
  136. }
  137.  
  138.  
  139. /* Adjust an array of longitude positions so that no two are within a    */
  140. /* certain orb of each other. This is used by the astro-graph routine to */
  141. /* make sure we don't draw any planet glyphs marking the lines on top of */
  142. /* each other. This is almost identical to the FillSymbolRing() routine  */
  143. /* used by the wheel charts; however, there the glyphs are placed in a   */
  144. /* continuous ring, while here we have the left and right screen edges.  */
  145. /* Also, here we are placing two sets of planets at the same time.       */
  146.  
  147. void FillSymbolLine(symbol)
  148. real *symbol;
  149. {
  150.   real orb = DEFORB*1.35*(real)gi.nScale, max = rDegMax, k1, k2, temp;
  151.   int i, j, k = 1, l;
  152.  
  153.   if (gi.nMode != gEphemeris)
  154.     max *= (real)gi.nScale;
  155.   else
  156.     orb *= rDegMax/(real)gs.xWin;
  157.  
  158.   /* Keep adjusting as long as we can still make changes. */
  159.  
  160.   for (l = 0; k && l < us.nDivision*2; l++) {
  161.     k = 0;
  162.     for (i = 1; i <= cObj*2; i++)
  163.       if (FProper((i+1)/2) && symbol[i] >= 0.0) {
  164.  
  165.         /* For each object, determine who is closest to the left and right. */
  166.  
  167.         k1 = max-symbol[i]; k2 = -symbol[i];
  168.         for (j = 1; j <= cObj*2; j++) {
  169.           if (FProper((j+1)/2) && i != j) {
  170.             temp = symbol[j]-symbol[i];
  171.             if (temp < k1 && temp >= 0.0)
  172.               k1 = temp;
  173.             else if (temp > k2 && temp <= 0.0)
  174.               k2 = temp;
  175.           }
  176.         }
  177.  
  178.         /* If an object's too close on one side, then we move to the other. */
  179.  
  180.         if (k2 > -orb && k1 > orb) {
  181.           k = 1; symbol[i] = symbol[i]+orb*0.51+k2*0.49;
  182.         } else if (k1 < orb && k2 < -orb) {
  183.           k = 1; symbol[i] = symbol[i]-orb*0.51+k1*0.49;
  184.         } else if (k2 > -orb && k1 < orb) {
  185.           k = 1; symbol[i] = symbol[i]+(k1+k2)*0.5;
  186.         }
  187.       }
  188.   }
  189. }
  190.  
  191.  
  192. /* Given a zodiac degree, adjust it if need be to account for the expanding */
  193. /* and compacting of parts the zodiac that happen when we display a graphic */
  194. /* wheel chart such that all the houses appear the same size.               */
  195.  
  196. real HousePlaceInX(deg)
  197. real deg;
  198. {
  199.   int in;
  200.  
  201.   if (gi.nMode == gWheel)    /* We only adjust for the -w -X combination. */
  202.     return deg;
  203.   in = HousePlaceIn(deg);
  204.   return Mod(ZFromS(in)+MinDistance(house[in], deg)/
  205.     MinDistance(house[in], house[Mod12(in+1)])*30.0);
  206. }
  207.  
  208.  
  209. /*
  210. ******************************************************************************
  211. ** Multiple Chart Graphics Routines.
  212. ******************************************************************************
  213. */
  214.  
  215. /* Draw another wheel chart; however, this time we have two rings of planets */
  216. /* because we are doing a relationship chart between two sets of data. This  */
  217. /* chart is obtained when the -r0 is combined with the -X switch.            */
  218.  
  219. void XChartWheelRelation()
  220. {
  221.   real xsign[cSign+1], xhouse1[cSign+1], xplanet1[objMax], xplanet2[objMax], symbol[objMax];
  222.   int cx, cy, i, j;
  223.   real asc, unitx, unity, px, py, temp;
  224.  
  225.   /* Set up variables and temporarily automatically decrease the horizontal */
  226.   /* chart size to leave room for the sidebar if that mode is in effect.    */
  227.  
  228.   if (gs.fText && !us.fVelocity)
  229.     gs.xWin -= xSideT;
  230.   cx = gs.xWin/2 - 1; cy = gs.yWin/2 - 1;
  231.   unitx = (real)cx; unity = (real)cy;
  232.   asc = gs.nLeft ? cp1.obj[abs(gs.nLeft)]+90*(gs.nLeft < 0) : cp1.cusp[1];
  233.  
  234.   /* Fill out arrays with the degree of each object, cusp, and sign glyph. */
  235.  
  236.   if (gi.nMode == gWheel) {
  237.     for (i = 1; i <= cSign; i++)
  238.       xhouse1[i] = PZ(cp1.cusp[i]);
  239.   } else {
  240.     asc -= cp1.cusp[1];
  241.     for (i = 1; i <= cSign; i++)
  242.       xhouse1[i] = PZ(ZFromS(i));
  243.   }
  244.   for (i = 1; i <= cSign; i++)
  245.     xsign[i] = PZ(HousePlaceInX(ZFromS(i)));
  246.   for (i = 1; i <= cObj; i++)
  247.     xplanet1[i] = PZ(HousePlaceInX(cp1.obj[i]));
  248.   for (i = 1; i <= cObj; i++)
  249.     xplanet2[i] = PZ(HousePlaceInX(cp2.obj[i]));
  250.  
  251.   /* Draw the horizon and meridian lines across whole chart, and draw the */
  252.   /* zodiac and house rings, exactly like before. We are drawing only the */
  253.   /* houses of one of the two charts in the relationship, however.        */
  254.  
  255.   DrawColor(gi.kiLite);
  256.   DrawDash(cx+POINT1(unitx, 0.99, PX(xhouse1[sAri])),
  257.            cy+POINT1(unity, 0.99, PY(xhouse1[sAri])),
  258.            cx+POINT1(unitx, 0.99, PX(xhouse1[sLib])),
  259.            cy+POINT1(unity, 0.99, PY(xhouse1[sLib])), !gs.fColor);
  260.  
  261.   DrawDash(cx+POINT1(unitx, 0.99, PX(xhouse1[sCap])),
  262.            cy+POINT1(unity, 0.99, PY(xhouse1[sCap])),
  263.            cx+POINT1(unitx, 0.99, PX(xhouse1[sCan])),
  264.            cy+POINT1(unity, 0.99, PY(xhouse1[sCan])), !gs.fColor);
  265.  
  266.   for (i = 0; i < nDegMax; i += 5-(gs.fColor || gs.fPS || gs.fMeta)*4) 
  267.   {
  268.     temp = PZ(HousePlaceInX((real)i));
  269.     px = PX(temp); py = PY(temp);
  270.     DrawColor(i%5 ? gi.kiGray : gi.kiOn);
  271.     DrawDash(cx+POINT1(unitx, 0.78, px), cy+POINT1(unity, 0.78, py),
  272.              cx+POINT2(unitx, 0.82, px), cy+POINT2(unity, 0.82, py),  ((gs.fPS || gs.fMeta) && i%5)*2);
  273.   }
  274.  
  275.   DrawColor(gi.kiOn);
  276.   DrawCircle(cx, cy, (int)(unitx*0.95+rRound), (int)(unity*0.95+rRound));
  277.   DrawCircle(cx, cy, (int)(unitx*0.82+rRound), (int)(unity*0.82+rRound));
  278.   DrawCircle(cx, cy, (int)(unitx*0.78+rRound), (int)(unity*0.78+rRound));
  279.   DrawCircle(cx, cy, (int)(unitx*0.70+rRound), (int)(unity*0.70+rRound));
  280.  
  281.   for (i = 1; i <= cSign; i++) {
  282.     temp = xsign[i];
  283.     DrawColor(gi.kiOn);
  284.     DrawLine(cx+POINT2(unitx, 0.95, PX(temp)),
  285.              cy+POINT2(unity, 0.95, PY(temp)),
  286.              cx+POINT1(unitx, 0.82, PX(temp)),
  287.              cy+POINT1(unity, 0.82, PY(temp)));
  288.     DrawLine(cx+POINT2(unitx, 0.78, PX(xhouse1[i])),
  289.              cy+POINT2(unity, 0.78, PY(xhouse1[i])),
  290.              cx+POINT1(unitx, 0.70, PX(xhouse1[i])),
  291.              cy+POINT1(unity, 0.70, PY(xhouse1[i])));
  292.     if (gs.fColor && i%3 != 1) {
  293.       DrawColor(gi.kiGray);
  294.       DrawDash(cx, cy, cx+POINT1(unitx, 0.70, PX(xhouse1[i])),
  295.                        cy+POINT1(unity, 0.70, PY(xhouse1[i])), 1);
  296.     }
  297.     temp = Midpoint(temp, xsign[Mod12(i+1)]);
  298.     DrawColor(kSignB(i));
  299.     DrawSign(i, cx+POINT1(unitx, 0.885, PX(temp)),
  300.                 cy+POINT1(unity, 0.885, PY(temp)));
  301.     temp = Midpoint(xhouse1[i], xhouse1[Mod12(i+1)]);
  302.     DrawHouse(i, cx+POINT1(unitx, 0.74, PX(temp)),
  303.                  cy+POINT1(unity, 0.74, PY(temp)));
  304.   }
  305.  
  306.   /* Draw the outer ring of planets (based on the planets in the chart     */
  307.   /* which the houses do not reflect - the houses belong to the inner ring */
  308.   /* below). Draw each glyph, a line from it to its actual position point  */
  309.   /* in the outer ring, and then draw another line from this point to a    */
  310.   /* another dot at the same position in the inner ring as well.           */
  311.  
  312.   for (i = 1; i <= cObj; i++)
  313.     symbol[i] = xplanet2[i];
  314.  
  315.   FillSymbolRing(symbol);
  316.  
  317.   for (i = cObj; i >= 1; i--) if (FProper2(i)) 
  318.   {
  319.     if (gs.fLabel) {
  320.       temp = symbol[i];
  321.       DrawColor(cp2.dir[i] < 0.0 ? gi.kiGray : gi.kiOn);
  322.       DrawDash(cx+POINT1(unitx, 0.58, PX(xplanet2[i])),
  323.                cy+POINT1(unity, 0.58, PY(xplanet2[i])),
  324.                cx+POINT2(unitx, 0.61, PX(temp)),
  325.                cy+POINT2(unity, 0.61, PY(temp)), (cp2.dir[i] < 0.0 ? 1 : 0) - gs.fColor);
  326.  
  327.       DrawObject(i, cx+POINT1(unitx, 0.65, PX(temp)),
  328.                     cy+POINT1(unity, 0.65, PY(temp)));
  329.     }
  330.     DrawColor(kObjB[i]);
  331.     DrawPoint(cx+POINT1(unitx, 0.56, PX(xplanet2[i])),
  332.               cy+POINT1(unity, 0.56, PY(xplanet2[i])));
  333.     DrawPoint(cx+POINT1(unitx, 0.43, PX(xplanet2[i])),
  334.               cy+POINT1(unity, 0.43, PY(xplanet2[i])));
  335.     DrawColor(cp2.dir[i] < 0.0 ? gi.kiGray : gi.kiOn);
  336.     DrawDash(cx+POINT1(unitx, 0.45, PX(xplanet2[i])),
  337.              cy+POINT1(unity, 0.45, PY(xplanet2[i])),
  338.              cx+POINT2(unitx, 0.54, PX(xplanet2[i])),
  339.              cy+POINT2(unity, 0.54, PY(xplanet2[i])), 2-gs.fColor);
  340.   }
  341.  
  342.   /* Now draw the inner ring of planets. If it weren't for the outer ring,  */
  343.   /* this would be just like the standard non-relationship wheel chart with */
  344.   /* only one set of planets. Again, draw glyph, and a line to true point.  */
  345.  
  346.   for (i = 1; i <= cObj; i++)
  347.     symbol[i] = xplanet1[i];
  348.  
  349.   FillSymbolRing(symbol);
  350.  
  351.   for (i = 1; i <= cObj; i++) if (FProper(i))
  352.   {
  353.     if (gs.fLabel) {
  354.       temp = symbol[i];
  355.       DrawColor(cp1.dir[i] < 0.0 ? gi.kiGray : gi.kiOn);
  356.       DrawDash(cx+POINT1(unitx, 0.45, PX(xplanet1[i])),
  357.                cy+POINT1(unity, 0.45, PY(xplanet1[i])),
  358.                cx+POINT2(unitx, 0.48, PX(temp)),
  359.                cy+POINT2(unity, 0.48, PY(temp)), (cp1.dir[i] < 0.0 ? 1 : 0) - gs.fColor);
  360.       DrawObject(i, cx+POINT1(unitx, 0.52, PX(temp)),
  361.                     cy+POINT1(unity, 0.52, PY(temp)));
  362.     } else
  363.       DrawColor(kObjB[i]);
  364.     DrawPoint(cx+POINT1(unitx, 0.43, PX(xplanet1[i])),
  365.               cy+POINT1(unity, 0.43, PY(xplanet1[i])));
  366.   }
  367.  
  368.   /* Draw lines connecting planets between the two charts that have aspects. */
  369.  
  370.   if (!gs.fAlt)                       /* Don't draw aspects in bonus mode. */
  371.   {
  372.     if (!FCreateGridRelation(fFalse))
  373.       return;
  374.  
  375.     for (j = cObj; j >= 1; j--)
  376.       for (i = cObj; i >= 1; i--)
  377.         if (grid->n[i][j] && FProper2(i) && FProper(j)) {
  378.           DrawColor(kAspB[grid->n[i][j]]);
  379.           DrawDash(cx+POINT1(unitx, 0.41, PX(xplanet1[j])),
  380.                    cy+POINT1(unity, 0.41, PY(xplanet1[j])),
  381.                    cx+POINT1(unitx, 0.41, PX(xplanet2[i])),
  382.                    cy+POINT1(unity, 0.41, PY(xplanet2[i])), abs(grid->v[i][j]/60/2));
  383.         }
  384.   }
  385.  
  386.   /* Go draw sidebar with chart information and positions if need be. */
  387.  
  388.   DrawInfo();
  389. }
  390.  
  391.  
  392. /* Draw an aspect (or midpoint) grid in the window, between the planets in  */
  393. /* two different charts, with the planets labeled at the top and side. This */
  394. /* chart is done when the -g switch is combined with the -r0 and -X switch. */
  395. /* Like above, the chart always has a (definable) fixed number of cells.    */
  396.  
  397. void XChartGridRelation()
  398. {
  399.   char sz[cchSzDef];
  400.   int unit, siz, x, y, i, j, k, l;
  401.   KI c;
  402.  
  403.   unit = CELLSIZE*gi.nScale; siz = (gs.nGridCell+1)*unit;
  404.   if (!FCreateGridRelation(gs.fAlt != us.fGridConfig))
  405.     return;
  406.   for (y = 0, j = -1; y <= gs.nGridCell; y++) {
  407.     do {
  408.       j++;
  409.     } while (ignore[j] && j <= cObj);
  410.     DrawColor(gi.kiGray);
  411.     DrawDash(0, (y+1)*unit, siz, (y+1)*unit, !gs.fColor);
  412.     DrawDash((y+1)*unit, 0, (y+1)*unit, siz, !gs.fColor);
  413.     DrawColor(gi.kiLite);
  414.     DrawEdge(0, y*unit, unit, (y+1)*unit);
  415.     DrawEdge(y*unit, 0, (y+1)*unit, unit);
  416.     if (j <= cObj) for (x = 0, i = -1; x <= gs.nGridCell; x++) {
  417.       do {
  418.         i++;
  419.       } while (ignore[i] && i <= cObj);
  420.  
  421.       /* Again, we are looping through each cell in each row and column. */
  422.  
  423.       if (i <= cObj) {
  424.         gi.xTurtle = x*unit+unit/2;
  425.         gi.yTurtle = y*unit+unit/2 - (gi.nScale/gi.nScaleT > 2 ? 5*gi.nScaleT : 0);
  426.         k = grid->n[i][j];
  427.  
  428.         /* If current cell is on top row or left hand column, draw glyph */
  429.         /* of planet owning the particular row or column in question.    */
  430.  
  431.         if (y == 0 || x == 0) {
  432.           if (x+y > 0)
  433.             DrawObject(j == 0 ? i : j, gi.xTurtle, gi.yTurtle);
  434.         } else {
  435.  
  436.         /* Otherwise, draw glyph of aspect in effect, or glyph of */
  437.         /* sign of midpoint, between the two planets in question. */
  438.  
  439.           if (gs.fAlt == us.fGridConfig) {
  440.             if (k) {
  441.               DrawColor(c = kAspB[k]);
  442.               DrawAspect(k, gi.xTurtle, gi.yTurtle);
  443.             }
  444.           } else {
  445.             DrawColor(c = kSignB(grid->n[i][j]));
  446.             DrawSign(grid->n[i][j], gi.xTurtle, gi.yTurtle);
  447.           }
  448.         }
  449.  
  450.         /* Again, when scale size is 300+, print some text in current cell: */
  451.  
  452.         if (gi.nScale/gi.nScaleT > 2 && gs.fLabel) {
  453.  
  454.           /* For top and left edges, print sign and degree of the planet. */
  455.  
  456.           if (y == 0 || x == 0) 
  457.           {
  458.             if (x+y > 0) {
  459.               k = SFromZ(y == 0 ? cp2.obj[i] : cp1.obj[j]);
  460.               l = (int)((y == 0 ? cp2.obj[i] : cp1.obj[j])-ZFromS(k));
  461.               c = kSignB(k);
  462.               sprintf(sz, "%c%c%c %02d", chSig3(k), l);
  463.  
  464.               /* For extreme upper left corner, print some little arrows */
  465.               /* pointing out chart1's planets and chart2's planets.     */
  466.  
  467.             } else {
  468.               c = gi.kiLite;
  469.               sprintf(sz, "1v 2->");
  470.             }
  471.           } else {
  472.             k = abs(grid->v[i][j]);
  473.  
  474.             /* For aspect cells, print the orb in degrees and minutes. */
  475.  
  476.             if (gs.fAlt == us.fGridConfig) {
  477.               if (grid->n[i][j])
  478.                 sprintf(sz, "%c%d %02d'", k != grid->v[i][j] ?
  479.                   (us.fAppSep ? 'a' : '-') : (us.fAppSep ? 's' : '+'),
  480.                   k/60, k%60);
  481.               else
  482.                 sprintf(sz, "");
  483.  
  484.             /* For midpoint cells, print degree and minute. */
  485.  
  486.             } else
  487.               sprintf(sz, "%2d %02d'", k/60, k%60);
  488.           }
  489.           DrawColor(c);
  490.           DrawSz(sz, x*unit+unit/2, (y+1)*unit-3*gi.nScaleT, dtBottom);
  491.         }
  492.       }
  493.     }
  494.   }
  495. }
  496.  
  497.  
  498. #ifdef BIORHYTHM
  499. /* Draw a graphic biorhythm chart on the screen, as is done when the -rb    */
  500. /* switch is combined with -X. This is technically a relationship chart in  */
  501. /* that biorhythm status is determined by a natal chart time at another     */
  502. /* later time. For the day in question, and for two weeks before and after, */
  503. /* the Physical, Emotional, and Mental percentages are plotted.             */
  504.  
  505. void XChartBiorhythm()
  506. {
  507.   char sz[6], *c;
  508.   real jd, r, a;
  509.   int x1, x2, xs, cx, y1, y2, ys, cy, i, j, k, x, y, x0, y0;
  510.  
  511.   k = xFont*6*gi.nScaleT;
  512.   x1 = k; x2 = gs.xWin-k; xs = x2-x1; cx = (x1+x2)/2;
  513.   k = CELLSIZE;
  514.   y1 = k; y2 = gs.yWin-k; ys = y2-y1; cy = (y1+y2)/2;
  515.  
  516.   /* Create a dotted day/percentage grid to graph on. */
  517.  
  518.   DrawColor(gi.kiGray);
  519.   DrawDash(x1, cy, x2, cy, 1);
  520.   DrawDash(cx, y1, cx, y2, 1);
  521.   for (j = -BIODAYS+1; j <= BIODAYS-1; j++) {
  522.     x = x1 + NMultDiv(xs, j+BIODAYS, BIODAYS*2);
  523.     for (k = -90; k <= 90; k += 10) {
  524.       y = y1 + NMultDiv(ys, 100+k, 200);
  525.       DrawPoint(x, y);
  526.     }
  527.   }
  528.  
  529.   /* Now actually draw the three biorhythm curves. */
  530.  
  531.   for (i = 1; i <= 3; i++) {
  532.     jd = RFloor(is.JD + rRound);
  533.     switch (i) {
  534.     case 1: r = brPhy; c = "PHYS"; j = eFir; break;
  535.     case 2: r = brEmo; c = "EMOT"; j = eWat; break;
  536.     case 3: r = brInt; c = "INTE"; j = eEar; break;
  537.     }
  538.     DrawColor(kElemB[j]);
  539.     for (jd -= (real)BIODAYS, j = -BIODAYS; j <= BIODAYS; j++, jd += 1.0) {
  540.       a = RBiorhythm(jd, r);
  541.       x = x1 + NMultDiv(xs, j+BIODAYS, BIODAYS*2);
  542.       y = y1 + (int)((real)ys * (100.0-a) / 200.0);
  543.       if (j > -BIODAYS)
  544.         DrawLine(x0, y0, x, y);
  545.       else
  546.         DrawSz(c, x1/2, y+2*gi.nScaleT, dtCent);
  547.       x0 = x; y0 = y;
  548.     }
  549.   }
  550.  
  551.   DrawColor(gi.kiLite);
  552.  
  553.   /* Label biorhythm percentages along right vertical axis. */
  554.  
  555.   for (k = -100; k <= 100; k += 10) 
  556.   {
  557.     sprintf(sz, "%c%3d%%", k < 0 ? '-' : '+', abs(k));
  558.     y = y1 + NMultDiv(ys, 100-k, 200);
  559.     DrawSz(sz, (x2+gs.xWin)/2, y+2*gi.nScaleT, dtCent);
  560.   }
  561.  
  562.   /* Label days on top horizontal axis. */
  563.  
  564.   for (j = -BIODAYS+2; j < BIODAYS; j += 2) 
  565.   {
  566.     x = x1 + NMultDiv(xs, j+BIODAYS, BIODAYS*2);
  567.     sprintf(sz, "%c%d", j < 0 ? '-' : '+', abs(j));
  568.     DrawSz(sz, x, y1-2*gi.nScaleT, dtBottom);
  569.   }
  570.   DrawEdge(x1, y1, x2, y2);
  571. }
  572. #endif /* BIORHYTHM */
  573. #endif /* GRAPH */
  574.  
  575. /* xcharts2.c */
  576.