home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / Astro / astrolog / Source / xcharts.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-12  |  38.2 KB  |  1,095 lines

  1. /*
  2. ** Astrolog (Version 4.10) File: xcharts.c
  3. **
  4. ** IMPORTANT NOTICE: the graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1994 by Walter D. Pullen
  6. ** (cruiser1@stein.u.washington.edu). Permission is granted to freely
  7. ** use and distribute these routines provided one doesn't sell,
  8. ** restrict, or profit from them in any way. Modification is allowed
  9. ** provided these notices remain with any altered or edited versions of
  10. ** the program.
  11. **
  12. ** The main planetary calculation routines used in this program have
  13. ** been Copyrighted and the core of this program is basically a
  14. ** conversion to C of the routines created by James Neely as listed in
  15. ** Michael Erlewine's 'Manual of Computer Programming for Astrologers',
  16. ** available from Matrix Software. The copyright gives us permission to
  17. ** use the routines for personal use but not to sell them or profit from
  18. ** them in any way.
  19. **
  20. ** The PostScript code within the core graphics routines are programmed
  21. ** and Copyright (C) 1992-1993 by Brian D. Willoughby
  22. ** (brianw@sounds.wa.com). Conditions are identical to those above.
  23. **
  24. ** The extended accurate ephemeris databases and formulas are from the
  25. ** calculation routines in the program "Placalc" and are programmed and
  26. ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl
  27. ** (alois@azur.ch). The use of that source code is subject to
  28. ** regulations made by Astrodienst Zurich, and the code is not in the
  29. ** public domain. This copyright notice must not be changed or removed
  30. ** by any user of this program.
  31. **
  32. ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991.
  33. ** X Window graphics initially programmed 10/23-29/1991.
  34. ** PostScript graphics initially programmed 11/29-30/1992.
  35. ** Last code change made 3/19/1994.
  36. */
  37.  
  38. #include "astrolog.h"
  39.  
  40. #ifdef GRAPH
  41.  
  42. /*
  43. ******************************************************************************
  44. ** Single Chart Graphics Subprograms.
  45. ******************************************************************************
  46. */
  47.  
  48. /* Given a string, draw it on the screen using the given color. The       */
  49. /* position of the text is based the saved positions of where we drew the */
  50. /* text the last time the routine was called, being either directly below */
  51. /* in the same column or in the same row just to the right. This is used  */
  52. /* by the sidebar drawing routine to print a list of text on the chart.   */
  53.  
  54. int DrawPrint(string, m, n)
  55. char *string;
  56. int m, n;
  57. {
  58.   static int x0, x, y;
  59.  
  60.   if (string == NULL) {    /* Null string means just initialize position. */
  61.     x0 = x = m; y = n;
  62.     return y;
  63.   }
  64.   if (y >= charty)    /* Don't draw if we've scrolled off the chart bottom. */
  65.     return y;
  66.   DrawColor(m);
  67.   DrawText(string, x, y, -1);
  68.  
  69.   /* If the second parameter is TRUE, we stay on the same line, otherwise */
  70.   /* when FALSE we go to the next line at the original column setting.    */
  71.  
  72.   if (n)
  73.     x += StringLen(string)*FONTX*scalet;
  74.   else {
  75.     x = x0;
  76.     n = y;
  77.     y += FONTY*scalet;
  78.   }
  79.   return y;
  80. }
  81.  
  82.  
  83. /* Print text showing the chart information and house and planet positions */
  84. /* of a chart in a "sidebar" to the right of the chart in question. This   */
  85. /* is always done for the -v and -w graphic wheel charts unless the -v0    */
  86. /* switch flag is also set, in which case none of the stuff here is done.  */
  87.  
  88. void DrawInfo()
  89. {
  90.   char string[STRING];
  91.   int elemode[4][3], elem[4], mo[3], tot, pos, abo, lef, lea, i, y, a, s;
  92.  
  93. #ifdef INTERPRET
  94.   /* Hack: Just for fun, if interpretation is active (which normally has  */
  95.   /* no effect whatsoever on graphics) we'll decorate the chart a little. */
  96.  
  97.   if (interpret) {
  98.     if (screenwidth & 1) {
  99.       /* If screenwidth value is odd, draw a moire pattern in each corner. */
  100.       abo = charty/(screenwidth/10);
  101.       lef = chartx/(screenwidth/10);
  102.       for (y = 0; y <= 1; y++)
  103.         for (i = 0; i <= 1; i++)
  104.           for (s = 0; s <= 1; s++)
  105.             for (a = 1; a < (s ? lef : abo)*2; a++) {
  106.               DrawColor(a & 1 ? gray : off);
  107.               DrawLine(i ? chartx-1-lef : lef, y ? charty-1-abo : abo,
  108.                 s ? (i ? chartx-1-a : a) : i*(chartx-1),
  109.                 s ? y*(charty-1) : (y ? charty-1-a : a));
  110.             }
  111.     } else {
  112.       /* If screenwidth is even, draw spider web lines in each corner. */
  113.       DrawColor(gray);
  114.       tot = screenwidth*3/20;
  115.       abo = charty/4;
  116.       lef = chartx/4;
  117.       for (y = 0; y <= 1; y++)
  118.         for (i = 0; i <= 1; i++)
  119.           for (a = 1; a < tot; a++)
  120.             DrawLine(i*(chartx-1), y ? (charty-1-a*abo/tot) : a*abo/tot,
  121.               i ? chartx-1-lef+a*lef/tot : lef-a*lef/tot, y*(charty-1));
  122.     }
  123.   }
  124. #endif
  125.   if (!xtext || (exdisplay & DASHv0) > 0)    /* Don't draw sidebar if */
  126.     return;                                  /* -v0 flag is set.      */
  127.  
  128.   a = ansi;
  129.   ansi = FALSE;
  130.   seconds = -seconds;
  131.   DrawColor(hilite);
  132.   if (xborder)
  133.     DrawLine(chartx-1, 0, chartx-1, charty-1);
  134.   chartx += SIDET;
  135.   DrawPrint(NULL, chartx-SIDET+FONTX*scalet, FONTY*7/5*scalet);
  136.  
  137.   /* Print chart header and setting information. */
  138.   sprintf(string, "%s %s", appname, VERSION);
  139.   DrawPrint(string, on, FALSE);
  140.   if (Mon == -1)
  141.     sprintf(string, "No time or space.");
  142.   else if (relation == DASHrc)
  143.     sprintf(string, "Composite chart.");
  144.   else {
  145.     sprintf(string, "%c%c%c %s", DAYNAM(DayOfWeek(Mon, Day, Yea)),
  146.       CharDate(Mon, Day, Yea, TRUE));
  147.     DrawPrint(string, hilite, FALSE);
  148.     DrawPrint(CharTime((int)floor(Tim),
  149.       (int)(FRACT(dabs(Tim))*100.0+ROUND/60.0)), hilite, TRUE);
  150.     sprintf(string, " (%s GMT)", CharZone(Zon));
  151.   }
  152.   DrawPrint(string, hilite, FALSE);
  153.   DrawPrint(CharLocation(Lon, Lat, 100.0), hilite, FALSE);
  154.   sprintf(string, "%s houses.", systemname[housesystem]);
  155.   DrawPrint(string, hilite, FALSE);
  156.   sprintf(string, "%s zodiac.", operation & DASHs ? "Siderial" : "Tropical");
  157.   DrawPrint(string, hilite, FALSE);
  158.   sprintf(string, "Julian Day = %10.3f", JulianDayFromTime(T));
  159.   DrawPrint(string, hilite, FALSE);
  160.  
  161.   /* Print house cusp positions. */
  162.   DrawPrint("", hilite, FALSE);
  163.   for (i = 1; i <= SIGNS; i++) {
  164.     sprintf(string, "%2d%s house: ", i, post[i]);
  165.     y = DrawPrint(string, signcolor(i), TRUE);
  166.     if (!seconds && (scale == 100 || !xfont || !xfile) && y < charty) {
  167.       s = scale;
  168.       scale = 100*scalet;
  169.       DrawSign(i, chartx-12*scalet, y-(FONTY/2-1)*scalet);
  170.       scale = s;
  171.     }
  172.     DrawPrint(CharZodiac(house[i]), signcolor(ZTOS(house[i])), FALSE);
  173.   }
  174.  
  175.   /* Print planet positions. */
  176.   DrawPrint("", hilite, FALSE);
  177.   for (i = 1; i <= BASE; i++) if (!ignore[i] && !IsCusp(i)) {
  178.     sprintf(string, seconds ? "%3.3s: " : "%4.4s: ", objectname[i]);
  179.     DrawPrint(string, objectcolor[i], TRUE);
  180.     y = DrawPrint(CharZodiac(planet[i]), signcolor(ZTOS(planet[i])), TRUE);
  181.     if (!seconds && i < S_LO &&
  182.       (scale == 100 || !xfont || !xfile) && y < charty) {
  183.       s = scale;
  184.       scale = 100*scalet;
  185.       DrawObject(i, chartx-12*scalet, y-(FONTY/2-1)*scalet);
  186.       scale = s;
  187.     }
  188.     sprintf(string, "%c ", ret[i] < 0.0 ? 'R' : ' ');
  189.     s = IsThing(i);
  190.     DrawPrint(string, on, s);
  191.     if (s)
  192.       DrawPrint(CharAltitude(planetalt[i]), hilite, FALSE);
  193.   }
  194.  
  195.   /* Print star positions. */
  196.   for (i = S_LO; i <= S_HI; i++) if (!ignore[i]) {
  197.     s = BASE+starname[i-BASE];
  198.     sprintf(string, seconds ? "%3.3s: " : "%4.4s: ", objectname[s]);
  199.     DrawPrint(string, objectcolor[s], TRUE);
  200.     DrawPrint(CharZodiac(planet[s]), signcolor(ZTOS(planet[s])), TRUE);
  201.     DrawPrint("  ", on, TRUE);
  202.     DrawPrint(CharAltitude(planetalt[s]), hilite, FALSE);
  203.   }
  204.  
  205.   /* Print element table information. */
  206.   DrawPrint("", hilite, FALSE);
  207.   CreateElemTable(elemode, elem, mo, &tot, &pos, &abo, &lef, &lea);
  208.   sprintf(string, "Fire: %d, Earth: %d,", elem[_FIR], elem[_EAR]);
  209.   DrawPrint(string, hilite, FALSE);
  210.   sprintf(string, "Air : %d, Water: %d", elem[_AIR], elem[_WAT]);
  211.   DrawPrint(string, hilite, FALSE);
  212.   sprintf(string, "Car: %d, Fix: %d, Mut: %d", mo[0], mo[1], mo[2]);
  213.   DrawPrint(string, hilite, FALSE);
  214.   sprintf(string, "Yang: %d, Yin: %d", pos, tot-pos);
  215.   DrawPrint(string, hilite, FALSE);
  216.   sprintf(string, "N: %d, S: %d, W: %d, E: %d", abo, tot-abo, tot-lef, lef); 
  217.   DrawPrint(string, hilite, FALSE);
  218.   seconds = -seconds;
  219.   ansi = a;
  220. }
  221.  
  222.  
  223. /* Draw a wheel chart, in which the 12 signs and houses are delineated, and  */
  224. /* the planets are inserted in their proper places. This is the default      */
  225. /* graphics chart to generate, as is done when the -v or -w (or no) switches */
  226. /* are included with -X. Draw the aspects in the middle of chart, too.       */
  227.  
  228. void XChartWheel()
  229. {
  230.   real xsign[SIGNS+1], xhouse[SIGNS+1], xplanet[TOTAL+1], symbol[TOTAL+1];
  231.   int cx, cy, i, j;
  232.   real asc, unitx, unity, px, py, temp;
  233.  
  234.   /* Set up variables and temporarily automatically decrease the horizontal */
  235.   /* chart size to leave room for the sidebar if that mode is in effect.    */
  236.  
  237.   if (xtext && !(exdisplay & DASHv0))
  238.     chartx -= SIDET;
  239.   cx = chartx/2 - 1; cy = charty/2 - 1;
  240.   unitx = (real)cx; unity = (real)cy;
  241.   asc = xeast ? planet[abs(xeast)]+90*(xeast < 0) : house[1];
  242.   InitCircle();
  243.  
  244.   /* Fill out arrays with the angular degree on the circle of where to    */
  245.   /* place each object, cusp, and sign glyph based on how the chart mode. */
  246.  
  247.   if (modex == MODEv) {
  248.     for (i = 1; i <= SIGNS; i++)
  249.       xhouse[i] = PZ(house[i]);
  250.   } else {
  251.     asc -= house[1];
  252.     for (i = 1; i <= SIGNS; i++)
  253.       xhouse[i] = PZ(STOZ(i));
  254.   }
  255.   for (i = 1; i <= SIGNS; i++)
  256.     xsign[i] = PZ(XHousePlaceIn(STOZ(i)));
  257.   for (i = 1; i <= total; i++)
  258.     xplanet[i] = PZ(XHousePlaceIn(planet[i]));
  259.  
  260.   /* Draw Ascendant/Descendant and Midheaven/Nadir lines across whole chart. */
  261.  
  262.   DrawColor(hilite);
  263.   DrawDash(cx+POINT(unitx, 0.99, PX(xhouse[1])),
  264.            cy+POINT(unity, 0.99, PY(xhouse[1])),
  265.            cx+POINT(unitx, 0.99, PX(xhouse[7])),
  266.            cy+POINT(unity, 0.99, PY(xhouse[7])), !xcolor);
  267.   DrawDash(cx+POINT(unitx, 0.99, PX(xhouse[10])),
  268.            cy+POINT(unity, 0.99, PY(xhouse[10])),
  269.            cx+POINT(unitx, 0.99, PX(xhouse[4])),
  270.            cy+POINT(unity, 0.99, PY(xhouse[4])), !xcolor);
  271.  
  272.   /* Draw small five or one degree increments around the zodiac sign ring. */
  273.  
  274.   for (i = 0; i < DEGD; i += 5-(xcolor || psfile || metafile)*4) {
  275.     temp = PZ(XHousePlaceIn((real)i));
  276.     px = PX(temp); py = PY(temp);
  277.     DrawColor(i%5 ? gray : on);
  278.     DrawDash(cx+POINT(unitx, 0.75, px), cy+POINT(unity, 0.75, py),
  279.       cx+POINT(unitx, 0.80, px), cy+POINT(unity, 0.80, py),
  280.       ((psfile || metafile) && i%5)*2);
  281.   }
  282.  
  283.   /* Draw circles for the zodiac sign and house rings. */
  284.  
  285.   DrawColor(on);
  286.   DrawCircle(cx, cy, (int)(unitx*0.95+ROUND), (int)(unity*0.95+ROUND));
  287.   DrawCircle(cx, cy, (int)(unitx*0.80+ROUND), (int)(unity*0.80+ROUND));
  288.   DrawCircle(cx, cy, (int)(unitx*0.75+ROUND), (int)(unity*0.75+ROUND));
  289.   DrawCircle(cx, cy, (int)(unitx*0.65+ROUND), (int)(unity*0.65+ROUND));
  290.  
  291.   /* Draw the glyphs for the signs and houses themselves. */
  292.  
  293.   for (i = 1; i <= SIGNS; i++) {
  294.     temp = xsign[i];
  295.     DrawColor(on);
  296.     DrawLine(cx+POINT(unitx, 0.95, PX(temp)),      /* Draw lines separating */
  297.       cy+POINT(unity, 0.95, PY(temp)),             /* each sign and house   */
  298.       cx+POINT(unitx, 0.80, PX(temp)),             /* from each other.      */
  299.       cy+POINT(unity, 0.80, PY(temp)));
  300.     DrawLine(cx+POINT(unitx, 0.75, PX(xhouse[i])),
  301.       cy+POINT(unity, 0.75, PY(xhouse[i])),
  302.       cx+POINT(unitx, 0.65, PX(xhouse[i])),
  303.       cy+POINT(unity, 0.65, PY(xhouse[i])));
  304.     if (xcolor && i%3 != 1) {                                 /* Lines from */
  305.       DrawColor(gray);                                        /* each house */
  306.       DrawDash(cx, cy, cx+POINT(unitx, 0.65, PX(xhouse[i])),  /* to center  */
  307.         cy+POINT(unity, 0.65, PY(xhouse[i])), 1);             /* of wheel.  */
  308.     }
  309.     temp = Midpoint(temp, xsign[Mod12(i+1)]);
  310.     DrawColor(signcolor(i));
  311.     DrawSign(i, cx+POINT(unitx, 0.875, PX(temp)),
  312.       cy+POINT(unity, 0.875, PY(temp)));
  313.     temp = Midpoint(xhouse[i], xhouse[Mod12(i+1)]);
  314.     DrawHouse(i, cx+POINT(unitx, 0.70, PX(temp)),
  315.       cy+POINT(unity, 0.70, PY(temp)));
  316.   }
  317.   for (i = 1; i <= total; i++)    /* Figure out where to put planet glyphs. */
  318.     symbol[i] = xplanet[i];
  319.   FillSymbolRing(symbol);
  320.  
  321.   /* For each planet, draw a small dot indicating where it is, and then */
  322.   /* a line from that point to the planet's glyph.                      */
  323.  
  324.   for (i = total; i >= 1; i--) if (Proper(i)) {
  325.     if (xlabel) {
  326.       temp = symbol[i];
  327.       DrawColor(ret[i] < 0.0 ? gray : on);
  328.       DrawDash(cx+POINT(unitx, 0.52, PX(xplanet[i])),
  329.         cy+POINT(unity, 0.52, PY(xplanet[i])),
  330.         cx+POINT(unitx, 0.56, PX(temp)),
  331.         cy+POINT(unity, 0.56, PY(temp)),
  332.         (ret[i] < 0.0 ? 1 : 0) - xcolor);
  333.       DrawObject(i, cx+POINT(unitx, 0.60, PX(temp)),
  334.         cy+POINT(unity, 0.60, PY(temp)));
  335.     } else
  336.       DrawColor(objectcolor[i]);
  337.     DrawPoint(cx+POINT(unitx, 0.50, PX(xplanet[i])),
  338.       cy+POINT(unity, 0.50, PY(xplanet[i])));
  339.   }
  340.  
  341.   /* Draw lines connecting planets which have aspects between them. */
  342.  
  343.   if (!xbonus) {          /* Don't draw aspects in bonus mode. */
  344.     CreateGrid(FALSE);
  345.     for (j = total; j >= 2; j--)
  346.       for (i = j-1; i >= 1; i--)
  347.         if (grid->n[i][j] && Proper(i) && Proper(j)) {
  348.           DrawColor(aspectcolor[grid->n[i][j]]);
  349.           DrawDash(cx+POINT(unitx, 0.48, PX(xplanet[i])),
  350.             cy+POINT(unity, 0.48, PY(xplanet[i])),
  351.             cx+POINT(unitx, 0.48, PX(xplanet[j])),
  352.             cy+POINT(unity, 0.48, PY(xplanet[j])),
  353.             abs(grid->v[i][j]/60/2));
  354.         }
  355.   }
  356.  
  357.   /* Go draw sidebar with chart information and positions if need be. */
  358.  
  359.   DrawInfo();
  360. }
  361.  
  362.  
  363. /* Draw an astro-graph chart on a map of the world, i.e. the draw the     */
  364. /* Ascendant, Descendant, Midheaven, and Nadir lines corresponding to the */
  365. /* time in the chart. This chart is done when the -L switch is combined   */
  366. /* with the -X switch.                                                    */
  367.  
  368. void XChartAstroGraph()
  369. {
  370.   real planet1[TOTAL+1], planet2[TOTAL+1],
  371.     end1[TOTAL*2+1], end2[TOTAL*2+1],
  372.     symbol1[TOTAL*2+1], symbol2[TOTAL*2+1],
  373.     lon = Lon, longm, x, y, z, ad, oa, am, od, dm, lat;
  374.   int unit = SCALE, stroke, lat1 = -60, lat2 = 75, y1, y2, xold1, xold2,
  375.     i, j, k, l;
  376.  
  377.   /* Erase top and bottom parts of map. We don't draw the astro-graph lines */
  378.   /* above certain latitudes, and this gives us room for glyph labels, too. */
  379.  
  380.   y1 = (91-lat1)*SCALE;
  381.   y2 = (91-lat2)*SCALE;
  382.   DrawColor(off);
  383.   DrawBlock(1, 1, chartx-2, y2-1);
  384.   DrawBlock(1, charty-2, chartx-2, y1+1);
  385.   DrawColor(hilite);
  386.   DrawDash(0, charty/2, chartx-2, charty/2, 4);    /* Draw equator. */
  387.   DrawColor(on);
  388.   DrawLine(1, y2, chartx-2, y2);
  389.   DrawLine(1, y1, chartx-2, y1);
  390.   for (i = 1; i <= total*2; i++)
  391.     end1[i] = end2[i] = -LARGE;
  392.  
  393.   /* Draw small hatches every 5 degrees along edges of world map. */
  394.  
  395.   DrawColor(hilite);
  396.   for (i = lat1; i <= lat2; i += 5) {
  397.     j = (91-i)*SCALE;
  398.     k = (2+(i%10 == 0)+2*(i%30 == 0))*scalet;
  399.     DrawLine(1, j, k, j);
  400.     DrawLine(chartx-2, j, chartx-1-k, j);
  401.   }
  402.   for (i = -180; i < 180; i += 5) {
  403.     j = (180-i)*SCALE;
  404.     k = (2+(i%10 == 0)+2*(i%30 == 0)+(i%90 == 0))*scalet;
  405.     DrawLine(j, y2+1, j, y2+k);
  406.     DrawLine(j, y1-1, j, y1-k);
  407.   }
  408.  
  409. #ifdef MATRIX
  410.   /* Calculate zenith locations of each planet. */
  411.  
  412.   for (i = 1; i <= total; i++) {
  413.     planet1[i] = DTOR(planet[i]);
  414.     planet2[i] = DTOR(planetalt[i]);
  415.     EclToEqu(&planet1[i], &planet2[i]);
  416.   }
  417.  
  418.   /* Draw the Midheaven lines and zenith location markings. */
  419.  
  420.   if (lon < 0.0)
  421.     lon += DEGREES;
  422.   for (i = 1; i <= total; i++) if (Proper(i)) {
  423.     x = DTOR(MC)-planet1[i];
  424.     if (x < 0.0)
  425.       x += 2.0*PI;
  426.     if (x > PI)
  427.       x -= 2.0*PI;
  428.     z = lon+RTOD(x);
  429.     if (z > DEGHALF)
  430.       z -= DEGREES;
  431.     j = (int) (Mod(DEGHALF-z+degree)*(real)SCALE);
  432.     DrawColor(elemcolor[_EAR]);
  433.     DrawLine(j, y1+unit*4, j, y2-unit*1);
  434.     end2[i*2-1] = (real) j;
  435.     y = RTOD(planet2[i]);
  436.     k = (int) ((91.0-y)*(real)SCALE);
  437.     DrawColor(hilite);
  438.     DrawBlock(j-1, k-1, j+1, k+1);
  439.     DrawColor(off);
  440.     DrawBlock(j, k, j, k);
  441.  
  442.     /* Draw Nadir lines assuming we aren't in bonus chart mode. */
  443.  
  444.     if (!xbonus) {
  445.       j += 180*SCALE;
  446.       if (j > chartx-2)
  447.         j -= (chartx-2);
  448.       end1[i*2-1] = (real) j;
  449.       DrawColor(elemcolor[_WAT]);
  450.       DrawLine(j, y1+unit*2, j, y2-unit*2);
  451.     }
  452.   }
  453.  
  454.   /* Now, normally, unless we are in bonus chart mode, we will go on to draw */
  455.   /* the Ascendant and Descendant lines here.                                */
  456.  
  457.   longm = DTOR(Mod(MC+lon));
  458.   if (!xbonus) for (i = 1; i <= total; i++) if (Proper(i)) {
  459.     xold1 = xold2 = -1000;
  460.  
  461.     /* Hack: Normally we draw the Ascendant and Descendant line segments  */
  462.     /* simultaneously. However, for the PostScript and metafile stroke    */
  463.     /* graphics, this will case the file to get inordinately large due to */
  464.     /* the constant thrashing between the Asc and Desc colors. Hence for  */
  465.     /* these charts only, we'll do two passes for Asc and Desc.           */
  466.     stroke = psfile || metafile;
  467.     for (l = 0; l <= stroke; l++)
  468.  
  469.     for (lat = (real)lat1; lat <= (real)lat2;
  470.       lat += 1.0/(real)(SCALE/scalet)) {
  471.  
  472.       /* First compute and draw the current segment of Ascendant line. */
  473.  
  474.       j = (int) ((91.0-lat)*(real)SCALE);
  475.       ad = tan(planet2[i])*tan(DTOR(lat));
  476.       if (ad*ad > 1.0)
  477.         ad = LARGE;
  478.       else {
  479.         ad = ASIN(ad);
  480.         oa = planet1[i]-ad;
  481.         if (oa < 0.0)
  482.           oa += 2.0*PI;
  483.         am = oa-PI/2.0;
  484.         if (am < 0.0)
  485.           am += 2.0*PI;
  486.         z = longm-am;
  487.         if (z < 0.0)
  488.           z += 2.0*PI;
  489.         if (z > PI)
  490.           z -= 2.0*PI;
  491.         z = RTOD(z);
  492.         k = (int) (Mod(DEGHALF-z+degree)*(real)SCALE);
  493.         if (!stroke || !l) {
  494.           DrawColor(elemcolor[_FIR]);
  495.           DrawWrap(xold1, j+scalet, k, j, 1, chartx-2);
  496.           if (lat == (real) lat1) {                        /* Line segment */
  497.             DrawLine(k, y1, k, y1+unit*4);                 /* pointing to  */
  498.             end2[i*2] = (real) k;                          /* Ascendant.   */
  499.           }
  500.         } else if (lat == (real) lat1)
  501.           end2[i*2] = (real) k;
  502.         xold1 = k;
  503.       }
  504.  
  505.       /* The curving Ascendant and Descendant lines actually touch each at  */
  506.       /* low latitudes. Sometimes when we start out, a particular planet's  */
  507.       /* lines haven't appeared yet, i.e. we are scanning at a latitude     */
  508.       /* where our planet's lines don't exist. If this is the case, then    */
  509.       /* when they finally do start, draw a thin horizontal line connecting */
  510.       /* the Ascendant and Descendant lines so they don't just start in     */
  511.       /* space. Note that these connected lines aren't labeled with glyphs. */
  512.  
  513.       if (ad == LARGE) {
  514.         if (xold1 >= 0) {
  515.           if (!stroke || !l) {
  516.             DrawColor(gray);
  517.             DrawWrap(xold1, j+1, xold2, j+1, 1, chartx-2);
  518.           }
  519.           lat = DEGQUAD;
  520.         }
  521.       } else {
  522.  
  523.       /* Then compute and draw corresponding segment of Descendant line. */
  524.  
  525.         od = planet1[i]+ad;
  526.         dm = od+PI/2.0;
  527.         z = longm-dm;
  528.         if (z < 0.0)
  529.           z += 2.0*PI;
  530.         if (z > PI)
  531.           z -= 2.0*PI;
  532.         z = RTOD(z);
  533.         k = (int) (Mod(DEGHALF-z+degree)*(real)SCALE);
  534.         if (xold2 < 0 && lat > (real)lat1 && (!stroke || l)) {
  535.           DrawColor(gray);
  536.           DrawWrap(xold1, j, k, j, 1, chartx-2);
  537.         }
  538.         if (!stroke || l) {
  539.           DrawColor(elemcolor[_AIR]);
  540.           DrawWrap(xold2, j+scalet, k, j, 1, chartx-2);
  541.           if (lat == (real)lat1)                           /* Line segment */
  542.             DrawLine(k, y1, k, y1+unit*2);                 /* pointing to  */
  543.         }                                                  /* Descendant.  */
  544.         xold2 = k;
  545.       }
  546.     }
  547. #endif /* MATRIX */
  548.  
  549.     /* Draw segments pointing to top of Ascendant and Descendant lines. */
  550.  
  551.     if (ad != LARGE) {
  552.       DrawColor(elemcolor[_FIR]);
  553.       DrawLine(xold1, y2, xold1, y2-unit*1);
  554.       DrawColor(elemcolor[_AIR]);
  555.       DrawLine(k, y2, k, y2-unit*2);
  556.       end1[i*2] = (real) k;
  557.     }
  558.   }
  559.   DrawColor(maincolor[5]);
  560.   DrawPoint((int)((181.0-Lon)*(real)SCALE),
  561.     (int)((91.0-Lat)*(real)SCALE));
  562.  
  563.   /* Determine where to draw the planet glyphs. We have four sets of each    */
  564.   /* planet - each planet's glyph appearing in the chart up to four times -  */
  565.   /* one for each type of line. The Midheaven and Ascendant lines are always */
  566.   /* labeled at the bottom of the chart, while the Nadir and Midheaven lines */
  567.   /* at the top. Therefore we need to place two sets of glyphs, twice.       */
  568.  
  569.   for (i = 1; i <= total*2; i++) {
  570.     symbol1[i] = end1[i];
  571.     symbol2[i] = end2[i];
  572.   }
  573.   FillSymbolLine(symbol1);
  574.   FillSymbolLine(symbol2);
  575.  
  576.   /* Now actually draw the planet glyphs. */
  577.  
  578.   for (i = 1; i <= total*2; i++) {
  579.     j = (i+1)/2;
  580.     if (Proper(j)) {
  581.       if ((turtlex = (int) symbol1[i]) > 0 && xlabel) {
  582.         DrawColor(ret[j] < 0.0 ? gray : on);
  583.         DrawDash((int) end1[i], y2-unit*2, (int) symbol1[i], y2-unit*4,
  584.           (ret[i] < 0.0 ? 1 : 0) - xcolor);
  585.         DrawObject(j, turtlex, y2-unit*10);
  586.       }
  587.       if ((turtlex = (int) symbol2[i]) > 0) {
  588.         DrawColor(ret[j] < 0.0 ? gray : on);
  589.         DrawDash((int) end2[i], y1+unit*4, (int) symbol2[i], y1+unit*8,
  590.           (ret[i] < 0.0 ? 1 : 0) - xcolor);
  591.         DrawObject(j, turtlex, y1+unit*14);
  592.         DrawTurtle(objectdraw[i & 1 ? _MC : _ASC], (int) symbol2[i],
  593.           y1+unit*24);
  594.       }
  595.     }
  596.   }
  597. }
  598.  
  599.  
  600. /* Draw an aspect and midpoint grid in the window, with planets labeled down */
  601. /* the diagonal. This chart is done when the -g switch is combined with the  */
  602. /* -X switch. The chart always has a certain number of cells; hence based    */
  603. /* how the restrictions are set up, there may be blank columns and rows,     */
  604. /* or else only the first number of unrestricted objects will be included.   */
  605.  
  606. void XChartGrid()
  607. {
  608.   char string[STRING];
  609.   int unit, siz, x, y, i, j, k;
  610.   colpal c;
  611.  
  612.   unit = CELLSIZE*SCALE; siz = gridobjects*unit;
  613.   CreateGrid(xbonus);
  614.  
  615.   /* Loop through each cell in each row and column of grid. */
  616.  
  617.   for (y = 1, j = 0; y <= gridobjects; y++) {
  618.     do {
  619.       j++;
  620.     } while (ignore[j] && j <= total);
  621.     DrawColor(gray);
  622.     DrawDash(0, y*unit, siz, y*unit, !xcolor);
  623.     DrawDash(y*unit, 0, y*unit, siz, !xcolor);
  624.     if (j <= total) for (x = 1, i = 0; x <= gridobjects; x++) {
  625.       do {
  626.         i++;
  627.       } while (ignore[i] && i <= total);
  628.       if (i <= total) {
  629.         turtlex = x*unit-unit/2;
  630.         turtley = y*unit-unit/2 - (SCALE/scalet > 2 ? 5*scalet : 0);
  631.  
  632.         /* If this is an aspect cell, draw glyph of aspect in effect. */
  633.  
  634.         if (xbonus ? x > y : x < y) {
  635.           DrawColor(c = aspectcolor[grid->n[i][j]]);
  636.           DrawAspect(grid->n[i][j], turtlex, turtley);
  637.  
  638.         /* If this is a midpoint cell, draw glyph of sign of midpoint. */
  639.  
  640.         } else if (xbonus ? x < y : x > y) {
  641.           DrawColor(c = signcolor(grid->n[i][j]));
  642.           DrawSign(grid->n[i][j], turtlex, turtley);
  643.  
  644.         /* For cells on main diagonal, draw glyph of planet. */
  645.  
  646.         } else {
  647.           DrawColor(hilite);
  648.           DrawEdge((y-1)*unit, (y-1)*unit, y*unit, y*unit);
  649.           DrawObject(i, turtlex, turtley);
  650.         }
  651.  
  652.         /* When the scale size is 300+, we can print text in each cell: */
  653.  
  654.         if (SCALE/scalet > 2 && xlabel) {
  655.           k = abs(grid->v[i][j]);
  656.  
  657.           /* For the aspect portion, print the orb in degrees and minutes. */
  658.  
  659.           if (xbonus ? x > y : x < y) {
  660.             if (grid->n[i][j])
  661.               sprintf(string, "%c%d %02d'", k != grid->v[i][j] ? '-' : '+',
  662.                 k/60, k%60);
  663.             else
  664.               sprintf(string, "");
  665.  
  666.           /* For the midpoint portion, print the degrees and minutes. */
  667.  
  668.           } else if (xbonus ? x < y : x > y)
  669.             sprintf(string, "%2d %02d'", k/60, k%60);
  670.  
  671.           /* For the main diagonal, print degree and sign of each planet. */
  672.  
  673.           else {
  674.             c = signcolor(grid->n[i][j]);
  675.             sprintf(string, "%c%c%c %02d", SIGNAM(grid->n[i][j]), k);
  676.           }
  677.           DrawColor(c);
  678.           DrawText(string, x*unit-unit/2, y*unit-3*scalet, TRUE);
  679.         }
  680.       }
  681.     }
  682.   }
  683. }
  684.  
  685.  
  686. /* Draw the local horizon, and draw in the planets where they are at the */
  687. /* time in question, as done when the -Z is combined with the -X switch. */
  688.  
  689. void XChartHorizon()
  690. {
  691.   real lon, lat, lonz[TOTAL+1], latz[TOTAL+1], azi[TOTAL+1], alt[TOTAL+1];
  692.   int x[TOTAL+1], y[TOTAL+1], m[TOTAL+1], n[TOTAL+1],
  693.     cx, cy, unit, x1, y1, x2, y2, xs, ys, i, j, k, l;
  694.   char string[2];
  695.  
  696.   unit = MAX(12, 6*SCALE);
  697.   x1 = unit; y1 = unit; x2 = chartx-1-unit; y2 = charty-1-unit;
  698.   unit = 12*SCALE;
  699.   xs = x2-x1; ys = y2-y1; cx = (x1+x2)/2; cy = (y1+y2)/2;
  700.  
  701.   /* Make a slightly smaller rectangle within the window to draw the planets */
  702.   /* in. Make segments on all four edges marking 5 degree increments.        */
  703.  
  704.   DrawColor(hilite);
  705.   for (i = 0; i <= 180; i += 5) {
  706.     j = y1+(int)((real)i*(real)ys/DEGHALF);
  707.     k = (2+(i%10 == 0)+2*(i%30 == 0))*scalet;
  708.     DrawLine(x1+1, j, x1+1+k, j);
  709.     DrawLine(x2-1, j, x2-1-k, j);
  710.   }
  711.   string[1] = '\0';
  712.   for (i = 0; i <= DEGD; i += 5) {
  713.     j = x1+(int)((real)i*(real)xs/DEGREES);
  714.     k = (2+(i%10 == 0)+2*(i%30 == 0))*scalet;
  715.     DrawLine(j, y1+1, j, y1+1+k);
  716.     DrawLine(j, y2-1, j, y2-1-k);
  717.     if (i % 90 == 0) {
  718.       *string = *dirname[i/90 & 3];
  719.       DrawText(string, j, y1-2*scalet, TRUE);
  720.     }
  721.   }
  722.  
  723.   /* Draw vertical lines dividing our rectangle into four areas. In our     */
  724.   /* local space chart, the middle line represents due south, the left line */
  725.   /* due east, the right line due west, and the edges due north. A fourth   */
  726.   /* horizontal line divides that which is above and below the horizon.     */
  727.  
  728.   DrawColor(gray);
  729.   DrawDash(cx, y1, cx, y2, 1);
  730.   DrawDash((cx+x1)/2, y1, (cx+x1)/2, y2, 1);
  731.   DrawDash((cx+x2)/2, y1, (cx+x2)/2, y2, 1);
  732.   DrawColor(on);
  733.   DrawEdge(x1, y1, x2, y2);
  734.   DrawDash(x1, cy, x2, cy, 1);
  735.  
  736.   /* Calculate the local horizon coordinates of each planet. First convert */
  737.   /* zodiac position and declination to zenith longitude and latitude.     */
  738.  
  739.   lon = DTOR(Mod(Lon)); lat = DTOR(Lat);
  740.   for (i = 1; i <= total; i++) {
  741.     lonz[i] = DTOR(planet[i]); latz[i] = DTOR(planetalt[i]);
  742.     EclToEqu(&lonz[i], &latz[i]);
  743.   }
  744.   for (i = 1; i <= total; i++) if (Proper(i)) {
  745.     lonz[i] = DTOR(Mod(RTOD(lonz[_MC]-lonz[i]+PI/2.0)));
  746.     EquToLocal(&lonz[i], &latz[i], PI/2.0-lat);
  747.     azi[i] = DEGREES-RTOD(lonz[i]); alt[i] = RTOD(latz[i]);
  748.     x[i] = x1+(int)((real)xs*(Mod(DEGQUAD-azi[i]))/DEGREES+ROUND);
  749.     y[i] = y1+(int)((real)ys*(DEGQUAD-alt[i])/DEGHALF+ROUND);
  750.     m[i] = x[i]; n[i] = y[i]+unit/2;
  751.   }
  752.  
  753.   /* As in the DrawGlobe() routine, we now determine where to draw the   */
  754.   /* glyphs in relation to the actual points, so that the glyphs aren't  */
  755.   /* drawn on top of each other if possible. Again, we assume that we'll */
  756.   /* put the glyph right under the point, unless there would be some     */
  757.   /* overlap and the above position is better off.                       */
  758.  
  759.   for (i = 1; i <= total; i++) if (Proper(i)) {
  760.     k = l = chartx+charty;
  761.     for (j = 1; j < i; j++) if (Proper(j)) {
  762.       k = MIN(k, abs(m[i]-m[j])+abs(n[i]-n[j]));
  763.       l = MIN(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j]));
  764.     }
  765.     if (k < unit || l < unit)
  766.       if (k < l)
  767.         n[i] -= unit;
  768.   }
  769.   for (i = total; i >= 1; i--) if (Proper(i))    /* Draw planet's glyph. */
  770.     DrawObject(i, m[i], n[i]);
  771.   for (i = total; i >= 1; i--) if (Proper(i)) {
  772.     DrawColor(objectcolor[i]);
  773.     if (!xbonus || i > BASE)
  774.       DrawPoint(x[i], y[i]);    /* Draw small or large dot */
  775.     else                        /* near glyph indicating   */
  776.       DrawSpot(x[i], y[i]);     /* exact local location.   */
  777.   }
  778. }
  779.  
  780.  
  781. /* Draw the local horizon, and draw in the planets where they are at the  */
  782. /* time in question. This chart is done when the -Z0 is combined with the */
  783. /* -X switch. This is an identical function to XChartHorizon(); however,  */
  784. /* that routine's chart is entered on the horizon and meridian. Here we   */
  785. /* center the chart around the center of the sky straight up from the     */
  786. /* local horizon, with the horizon itself being an encompassing circle.   */
  787.  
  788. void XChartHorizonSky()
  789. {
  790.   real lat, rx, ry, s, a, sqr2,
  791.     lonz[TOTAL+1], latz[TOTAL+1], azi[TOTAL+1], alt[TOTAL+1];
  792.   int x[TOTAL+1], y[TOTAL+1], m[TOTAL+1], n[TOTAL+1],
  793.     cx, cy, unit, x1, y1, x2, y2, xs, ys, i, j, k, l;
  794.  
  795.   unit = MAX(12, 6*SCALE);
  796.   x1 = unit; y1 = unit; x2 = chartx-1-unit; y2 = charty-1-unit;
  797.   unit = 12*SCALE;
  798.   xs = x2-x1; ys = y2-y1; cx = (x1+x2)/2; cy = (y1+y2)/2;
  799.  
  800.   /* Draw a circle in window to indicate horizon line, lines dividing   */
  801.   /* the window into quadrants to indicate n/s and w/e meridians, and   */
  802.   /* segments on these lines and the edges marking 5 degree increments. */
  803.  
  804.   sqr2 = sqrt(2.0);
  805.   DrawColor(gray);
  806.   DrawDash(cx, y1, cx, y2, 1);
  807.   DrawDash(x1, cy, x2, cy, 1);
  808.   DrawColor(hilite);
  809.   for (i = -125; i <= 125; i += 5) {
  810.     k = (2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0))*scalet;
  811.     s = 1.0/(DEGQUAD*sqr2);
  812.     j = cy+(int)(s*ys/2*i);
  813.     DrawLine(cx-k, j, cx+k, j);
  814.     j = cx+(int)(s*xs/2*i);
  815.     DrawLine(j, cy-k, j, cy+k);
  816.   }
  817.   for (i = 5; i < 55; i += 5) {
  818.     k = (2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0))*scalet;
  819.     s = 1.0/(DEGHALF-DEGQUAD*sqr2);
  820.     j = (int)(s*ys/2*i);
  821.     DrawLine(x1, y1+j, x1+k, y1+j);
  822.     DrawLine(x1, y2-j, x1+k, y2-j);
  823.     DrawLine(x2, y1+j, x2-k, y1+j);
  824.     DrawLine(x2, y2-j, x2-k, y2-j);
  825.     j = (int)(s*xs/2*i);
  826.     DrawLine(x1+j, y1, x1+j, y1+k);
  827.     DrawLine(x2-j, y1, x2-j, y1+k);
  828.     DrawLine(x1+j, y2, x1+j, y2-k);
  829.     DrawLine(x2-j, y2, x2-j, y2-k);
  830.   }
  831.   DrawText("N", cx, y1-2*scalet, TRUE);
  832.   DrawText("E", x1/2, cy+2*scalet, FALSE);
  833.   DrawText("W", (chartx+x2)/2, cy+2*scalet, FALSE);
  834.   if (!xtext)
  835.     DrawText("S", cx, charty-3*scalet, TRUE);
  836.   rx = xs/2/sqr2; ry = ys/2/sqr2;
  837.   DrawColor(on);
  838.   DrawEdge(x1, y1, x2, y2);
  839.   DrawCircle(cx, cy, (int)rx, (int)ry);
  840.   InitCircle();
  841.   for (i = 0; i < DEGD; i += 5) {
  842.     k = (2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0))*scalet;
  843.     DrawLine(cx+(int)((rx-k)*circ->x[i]), cy+(int)((ry-k)*circ->y[i]),
  844.       cx+(int)((rx+k)*circ->x[i]), cy+(int)((ry+k)*circ->y[i]));
  845.   }
  846.  
  847.   /* Calculate the local horizon coordinates of each planet. First convert */
  848.   /* zodiac position and declination to zenith longitude and latitude.     */
  849.  
  850.   lat = DTOR(Lat);
  851.   for (i = 1; i <= total; i++) {
  852.     lonz[i] = DTOR(planet[i]); latz[i] = DTOR(planetalt[i]);
  853.     EclToEqu(&lonz[i], &latz[i]);
  854.   }
  855.   for (i = 1; i <= total; i++) if (Proper(i)) {
  856.     lonz[i] = DTOR(Mod(RTOD(lonz[_MC]-lonz[i]+PI/2.0)));
  857.     EquToLocal(&lonz[i], &latz[i], PI/2.0-lat);
  858.     azi[i] = a = DEGREES-RTOD(lonz[i]); alt[i] = DEGQUAD-RTOD(latz[i]);
  859.     s = alt[i]/DEGQUAD;
  860.     x[i] = cx+(int)(rx*s*COSD(DEGHALF+azi[i])+ROUND);
  861.     y[i] = cy+(int)(ry*s*SIND(DEGHALF+azi[i])+ROUND);
  862.     if (!ISCHART(x[i], y[i]))
  863.       x[i] = -1000;
  864.     m[i] = x[i]; n[i] = y[i]+unit/2;
  865.   }
  866.  
  867.   /* As in the DrawGlobe() routine, we now determine where to draw the   */
  868.   /* glyphs in relation to the actual points, so that the glyphs aren't  */
  869.   /* drawn on top of each other if possible. Again, we assume that we'll */
  870.   /* put the glyph right under the point, unless there would be some     */
  871.   /* overlap and the above position is better off.                       */
  872.  
  873.   for (i = 1; i <= total; i++) if (Proper(i)) {
  874.     k = l = chartx+charty;
  875.     for (j = 1; j < i; j++) if (Proper(j)) {
  876.       k = MIN(k, abs(m[i]-m[j])+abs(n[i]-n[j]));
  877.       l = MIN(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j]));
  878.     }
  879.     if (k < unit || l < unit)
  880.       if (k < l)
  881.         n[i] -= unit;
  882.   }
  883.   for (i = total; i >= 1; i--) if (m[i] >= x1 && Proper(i))  /* Draw glyph. */
  884.     DrawObject(i, m[i], n[i]);
  885.   for (i = total; i >= 1; i--) if (x[i] >= y1 && Proper(i)) {
  886.     DrawColor(objectcolor[i]);
  887.     if (!xbonus || i > BASE)
  888.       DrawPoint(x[i], y[i]);    /* Draw small or large dot */
  889.     else                        /* near glyph indicating   */
  890.       DrawSpot(x[i], y[i]);     /* exact local location.   */
  891.   }
  892. }
  893.  
  894.  
  895. /* Draw a chart depicting an aerial view of the solar system in space, with */
  896. /* all the planets drawn around the Sun, and the specified central planet   */
  897. /* in the middle, as done when the -S is combined with the -X switch.       */
  898.  
  899. void XChartSpace()
  900. {
  901.   int x[TOTAL+1], y[TOTAL+1], m[TOTAL+1], n[TOTAL+1],
  902.     cx = chartx / 2, cy = charty / 2, unit, x1, y1, x2, y2, i, j, k, l;
  903.   real sx, sy, sz = 30.0, xp, yp, a;
  904.  
  905.   unit = MAX(xtext*12, 6*SCALE);
  906.   x1 = unit; y1 = unit; x2 = chartx-1-unit; y2 = charty-1-unit;
  907.   unit = 12*SCALE;
  908.  
  909.   /* Determine the scale of the chart. For a scale size of 400+, make the */
  910.   /* graphic 1 AU in radius (just out to Earth's orbit). For 300, make    */
  911.   /* the chart 6 AU in radius (enough for inner planets out to asteroid   */
  912.   /* belt). For a scale of 200, make window 30 AU in radius (enough for   */
  913.   /* planets out to Neptune). For scale of 100, make it 90 AU in radius   */
  914.   /* (enough for all planets including the orbits of the uranians.)       */
  915.  
  916.   if (SCALE < 2)
  917.     sz = 90.0;
  918.   else if (SCALE == 3)
  919.     sz = 6.0;
  920.   else if (SCALE > 3)
  921.     sz = 1.0;
  922.   sx = (real)(cx-x1)/sz; sy = (real)(cy-y1)/sz;
  923.   for (i = 0; i <= BASE; i++) if (Proper(i)) {
  924.  
  925.     /* Determine what glyph corresponds to our current planet. Normally the */
  926.     /* array indices are the same, however we have to do some swapping for  */
  927.     /* non-geocentric based charts where a planet gets replaced with Earth. */
  928.  
  929.     if (centerplanet == 0)
  930.       j = i < _MOO ? 1-i : i;
  931.     else if (centerplanet == 1)
  932.       j = i;
  933.     else
  934.       j = i == 0 ? centerplanet : (i == centerplanet ? 0 : i);
  935.     xp = spacex[j]; yp = spacey[j];
  936.     x[i] = cx-(int)(xp*sx); y[i] = cy+(int)(yp*sy);
  937.     m[i] = x[i]; n[i] = y[i]+unit/2;
  938.   }
  939.  
  940.   /* As in the DrawGlobe() routine, we now determine where to draw the   */
  941.   /* glyphs in relation to the actual points, so that the glyphs aren't  */
  942.   /* drawn on top of each other if possible. Again, we assume that we'll */
  943.   /* put the glyph right under the point, unless there would be some     */
  944.   /* overlap and the above position is better off.                       */
  945.  
  946.   for (i = 0; i <= BASE; i++) if (Proper(i)) {
  947.     k = l = chartx+charty;
  948.     for (j = 0; j < i; j++) if (Proper(j)) {
  949.       k = MIN(k, abs(m[i]-m[j])+abs(n[i]-n[j]));
  950.       l = MIN(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j]));
  951.     }
  952.     if (k < unit || l < unit)
  953.       if (k < l)
  954.         n[i] -= unit;
  955.   }
  956.  
  957.   /* Draw the 12 sign boundaries from the center body to edges of screen. */
  958.  
  959.   a = Mod(RTOD(Angle(spacex[_JUP], spacey[_JUP]))-planet[_JUP]);
  960.   DrawColor(gray);
  961.   for (i = 0; i < SIGNS; i++) {
  962.     k = cx+2*(int)((real)cx*COSD((real)i*30.0+a));
  963.     l = cy+2*(int)((real)cy*SIND((real)i*30.0+a));
  964.     DrawClip(cx, cy, k, l, x1, y1, x2, y2, 1);
  965.   }
  966.   DrawColor(hilite);
  967.   DrawEdge(x1, y1, x2, y2);
  968.   for (i = BASE; i >= 0; i--)
  969.     if (Proper(i) && ISLEGAL(m[i], n[i], x1, y1, x2, y2))
  970.       DrawObject(i, m[i], n[i]);
  971.   for (i = BASE; i >= 0; i--)
  972.     if (Proper(i) && ISLEGAL(x[i], y[i], x1, y1, x2, y2)) {
  973.       DrawColor(objectcolor[i]);
  974.       if (!xbonus || i > BASE)
  975.         DrawPoint(x[i], y[i]);    /* Draw small or large dot */
  976.       else                        /* near glyph indicating   */
  977.         DrawSpot(x[i], y[i]);     /* exact local location.   */
  978.     }
  979. }
  980.  
  981.  
  982. /* Draw a chart showing a graphical ephemeris for the given month (or year */
  983. /* if -Ey in effect), with the date on the vertical access and the zodiac  */
  984. /* on the horizontal, as done when the -E is combined with the -X switch.  */
  985.  
  986. void XChartEphemeris()
  987. {
  988.   real symbol[TOTAL*2+1];
  989.   char string[4];
  990.   int yea, unit = 6*SCALE, daytot, d = 1, day, mon, monsiz,
  991.     x1, y1, x2, y2, xs, ys, m, n, u, v, i, j;
  992.  
  993.   yea = (exdisplay & DASHEy) > 0;    /* Is this -Ey -X or just -E -X? */
  994.   if (yea) {
  995.     daytot = DayInYear(Yea);
  996.     day = 1; mon = 1; monsiz = 31;
  997.   } else
  998.     daytot = DayInMonth(Mon, Yea);
  999.   x1 = yea ? 30 : 24; y1 = unit*2; x2 = chartx - x1; y2 = charty - y1;
  1000.   xs = x2 - x1; ys = y2 - y1;
  1001.  
  1002.   /* Display glyphs of the zodiac along the bottom axis. */
  1003.   for (i = 1; i <= SIGNS+1; i++) {
  1004.     m = x1 + xs * (i-1) / 12;
  1005.     j = i > SIGNS ? 1 : i;
  1006.     DrawColor(signcolor(j));
  1007.     DrawSign(j, m, y2 + unit);
  1008.     DrawColor(gray);
  1009.     DrawDash(m, y1, m, y2, 2);
  1010.   }
  1011.  
  1012.   /* Loop and display planet movements for one day segment. */
  1013.   while (d <= daytot + 1) {
  1014.     n = v;
  1015.     v = y1 + MULTDIV(ys, d-1, daytot);
  1016.     if (!yea || day == 1) {
  1017.       DrawColor(gray);
  1018.       DrawDash(x1, v, x2, v, 1);    /* Marker line for day or month. */
  1019.     }
  1020.     if (d > 1)
  1021.       for (i = 1; i <= total; i++)
  1022.         planet1[i] = planet[i];
  1023.     if (yea) {
  1024.       MM = mon; DD = day;
  1025.     } else {
  1026.       MM = Mon; DD = d;
  1027.     }
  1028.     YY = Yea; TT = 0.0; ZZ = defzone; OO = deflong; AA = deflat;
  1029.     CastChart(TRUE);
  1030.  
  1031.     /* Draw planet glyphs along top of chart. */
  1032.     if (d < 2) {
  1033.       for (i = 1; i <= total; i++) {
  1034.         symbol[i*2-1] = -LARGE;
  1035.         if (!Proper(i) || (i == _MOO && xbonus))
  1036.           symbol[i*2] = -LARGE;
  1037.         else
  1038.           symbol[i*2] = planet[i];
  1039.       }
  1040.       FillSymbolLine(symbol);
  1041.       for (i = total; i >= 1; i--)
  1042.         if (symbol[i*2] >= 0.0)
  1043.           DrawObject(i, x1 + (int)((real)xs * symbol[i*2] / DEGREES), unit);
  1044.  
  1045.     /* Draw a line segment for each object during this time section. */
  1046.     } else
  1047.       for (i = total; i >= 1; i--) {
  1048.         if (!Proper(i) || (i == _MOO && xbonus))
  1049.           continue;
  1050.         m = x1 + (int)((real)xs * planet1[i] / DEGREES);
  1051.         u = x1 + (int)((real)xs * planet[i]  / DEGREES);
  1052.         DrawColor(objectcolor[i]);
  1053.         DrawWrap(m, n, u, v, x1, x2);
  1054.       }
  1055.  
  1056.     /* Label months or days in the month along the left and right edges. */
  1057.     if (d <= daytot && (!yea || day == 1)) {
  1058.       if (yea) {
  1059.         sprintf(string, "%c%c%c", MONNAM(mon));
  1060.         i = (mon == Mon);
  1061.       } else {
  1062.         sprintf(string, "%2d", d);
  1063.         i = (d == Day);
  1064.       }
  1065.       DrawColor(i ? on : hilite);
  1066.       DrawText(string,     FONTX   *scalet, v + (FONTY-2)*scalet, -1);
  1067.       DrawText(string, x2+(FONTX-1)*scalet, v + (FONTY-2)*scalet, -1);
  1068.     }
  1069.  
  1070.     /* Now increment the day counter. For a month we always go up by one. */
  1071.     /* For a year we go up by four or until the end of the month reached. */
  1072.     if (yea) {
  1073.       day += 4;
  1074.       if (day > monsiz) {
  1075.         d += 4-(day-monsiz-1);
  1076.         if (d <= daytot + 1) {
  1077.           mon++;
  1078.           monsiz = DayInMonth(mon, Yea);
  1079.           day = 1;
  1080.         }
  1081.       } else
  1082.         d += 4;
  1083.     } else
  1084.       d++;
  1085.   }
  1086.   DrawColor(hilite);
  1087.   DrawEdge(x1, y1, x2, y2);
  1088.  
  1089.   MM = Mon; DD = Day; TT = Tim;    /* Recast original chart. */
  1090.   CastChart(TRUE);
  1091. }
  1092. #endif /* GRAPH */
  1093.  
  1094. /* xcharts.c */
  1095.