home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Guide / c-cplusplus-interactive-guide.iso / c_ref / csource1 / ast40dos / xoptions.c < prev   
Encoding:
C/C++ Source or Header  |  1994-01-04  |  32.6 KB  |  965 lines

  1. /*
  2. ** Astrolog (Version 4.00) File: xoptions.c
  3. **
  4. ** IMPORTANT NOTICE: the graphics database and chart display routines
  5. ** used in this program are Copyright (C) 1991-1993 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 12/31/1993.
  36. */
  37.  
  38. #include "astrolog.h"
  39.  
  40. #ifdef GRAPH
  41.  
  42. /*
  43. ******************************************************************************
  44. ** Chart Graphics Subroutines.
  45. ******************************************************************************
  46. */
  47.  
  48. /* Return whether the specified object should be displayed in the current  */
  49. /* graphics chart type. For example, don't include the Moon in the solar   */
  50. /* system charts, don't include house cusps in astro-graph, and so on.     */
  51.  
  52. int Proper(i)
  53. int i;
  54. {
  55.   int j;
  56.  
  57.   if (modex == MODEL || modex == MODEE)       /* astro-graph or ephem charts */
  58.     j = IsThing(i);
  59.   else if (modex == MODEZ || modex == MODEG)  /* horizon or zenith charts */
  60.     j = IsObject(i);
  61.   else if (modex == MODES)                    /* solar system charts */
  62.     j = (i != _MOO && IsObject(i));
  63.   else
  64.     j = TRUE;
  65.   return j && !ignore[i];                     /* check restriction status */
  66. }
  67.  
  68.  
  69. /* Set up arrays with the sine and cosine values of each degree. This is   */
  70. /* used by the wheel chart routines which draw lots of circles. Memory is  */
  71. /* allocated for this array if not already done. The allocation and        */
  72. /* initialization is only done once, the first time the routine is called. */
  73.  
  74. bool InitCircle()
  75. {
  76.   char string[STRING];
  77.   int i;
  78.  
  79.   if (circ != NULL)
  80.     return TRUE;
  81.   Allocate(circ, sizeof(circlestruct), circlestruct PTR);
  82.   if (circ == NULL
  83. #ifdef PC
  84.     /* For PC's the array better not cross a segment boundary. */
  85.     || HIWORD(LOWORD(circ) + sizeof(circlestruct)) > 0
  86. #endif
  87.     ) {
  88.     sprintf(string, "Not enough memory for sine table (%d bytes).",
  89.       sizeof(circlestruct));
  90.     PrintError(string);
  91.     return FALSE;
  92.   }
  93.   for (i = 0; i < DEGR; i++) {
  94.     circ->x[i] = COSD((real) i);
  95.     circ->y[i] = SIND((real) i);
  96.   }
  97.   circ->x[DEGR] = circ->x[0]; circ->y[DEGR] = circ->y[0];
  98.   return TRUE;
  99. }
  100.  
  101.  
  102. /* Adjust an array of zodiac positions so that no two positions are within   */
  103. /* a certain orb of each other. This is used by the wheel drawing chart      */
  104. /* routines in order to make sure that we don't draw any planet glyphs on    */
  105. /* top of each other. We'll later draw the glyphs at the adjusted positions. */
  106.  
  107. void FillSymbolRing(symbol)
  108. real *symbol;
  109. {
  110.   real orb = DEFORB*256.0/(real)charty*(real)SCALE, k1, k2, temp;
  111.   int i, j, k = 1, l;
  112.  
  113.   /* Keep adjusting as long as we can still make changes, or until we do 'n' */
  114.   /* rounds. (With many objects, there just may not be enough room for all.) */
  115.  
  116.   for (l = 0; k && l < divisions*2; l++) {
  117.     k = 0;
  118.     for (i = 1; i <= total; i++) if (Proper(i)) {
  119.  
  120.       /* For each object, determine who is closest on either side. */
  121.  
  122.       k1 = LARGE; k2 = -LARGE;
  123.       for (j = 1; j <= total; j++)
  124.         if (Proper(j) && i != j) {
  125.           temp = symbol[j]-symbol[i];
  126.           if (dabs(temp) > DEGHALF)
  127.             temp -= DEGREES*Sgn(temp);
  128.           if (temp < k1 && temp >= 0.0)
  129.             k1 = temp;
  130.           else if (temp > k2 && temp <= 0.0)
  131.             k2 = temp;
  132.         }
  133.  
  134.       /* If an object's too close on one side, then we move to the other. */
  135.  
  136.       if (k2 > -orb && k1 > orb) {
  137.         k = 1; symbol[i] = Mod(symbol[i]+orb*0.51+k2*0.49);
  138.       } else if (k1 < orb && k2 < -orb) {
  139.         k = 1; symbol[i] = Mod(symbol[i]-orb*0.51+k1*0.49);
  140.  
  141.       /* If we are bracketed by close objects on both sides, then let's move */
  142.       /* to the midpoint, so we are as far away as possible from either one. */
  143.  
  144.       } else if (k2 > -orb && k1 < orb) {
  145.         k = 1; symbol[i] = Mod(symbol[i]+(k1+k2)*0.5);
  146.       }
  147.     }
  148.   }
  149. }
  150.  
  151.  
  152. /* Adjust an array of longitude positions so that no two are within a    */
  153. /* certain orb of each other. This is used by the astro-graph routine to */
  154. /* make sure we don't draw any planet glyphs marking the lines on top of */
  155. /* each other. This is almost identical to the FillSymbolRing() routine  */
  156. /* used by the wheel charts; however, there the glyphs are placed in a   */
  157. /* continuous ring, while here we have the left and right screen edges.  */
  158. /* Also, here we are placing two sets of planets at the same time.       */
  159.  
  160. void FillSymbolLine(symbol)
  161. real *symbol;
  162. {
  163.   real orb = DEFORB*1.35*(real)SCALE, max = DEGREES, k1, k2, temp;
  164.   int i, j, k = 1, l;
  165.  
  166.   if (modex != MODEE)
  167.     max *= (real)SCALE;
  168.   else
  169.     orb *= DEGREES/(real)chartx;
  170.  
  171.   /* Keep adjusting as long as we can still make changes. */
  172.  
  173.   for (l = 0; k && l < divisions*2; l++) {
  174.     k = 0;
  175.     for (i = 1; i <= total*2; i++)
  176.       if (Proper((i+1)/2) && symbol[i] >= 0.0) {
  177.  
  178.         /* For each object, determine who is closest to the left and right. */
  179.  
  180.         k1 = max-symbol[i]; k2 = -symbol[i];
  181.         for (j = 1; j <= total*2; j++) {
  182.           if (Proper((j+1)/2) && i != j) {
  183.             temp = symbol[j]-symbol[i];
  184.             if (temp < k1 && temp >= 0.0)
  185.               k1 = temp;
  186.             else if (temp > k2 && temp <= 0.0)
  187.               k2 = temp;
  188.           }
  189.         }
  190.  
  191.         /* If an object's too close on one side, then we move to the other. */
  192.  
  193.         if (k2 > -orb && k1 > orb) {
  194.           k = 1; symbol[i] = symbol[i]+orb*0.51+k2*0.49;
  195.         } else if (k1 < orb && k2 < -orb) {
  196.           k = 1; symbol[i] = symbol[i]-orb*0.51+k1*0.49;
  197.         } else if (k2 > -orb && k1 < orb) {
  198.           k = 1; symbol[i] = symbol[i]+(k1+k2)*0.5;
  199.         }
  200.       }
  201.   }
  202. }
  203.  
  204.  
  205. /* Another stream reader, this one is used by the globe drawing routine: */
  206. /* for the next body of land/water, return its name (and color), its     */
  207. /* longitude and latitude, and a vector description of its outline.      */
  208.  
  209. int ReadWorldData(nam, loc, lin)
  210. char **nam, **loc, **lin;
  211. {
  212.   static char FAR **datapointer = worlddata;
  213.  
  214.   *loc = *datapointer++;
  215.   *lin = *datapointer++;
  216.   *nam = *datapointer++;
  217.   if (*loc[0]) {
  218.     if ((exdisplay & DASHXP0) && xfile)
  219.       fprintf(stdout, "%s\n", *nam+1);
  220.     return TRUE;
  221.   }
  222.   datapointer = worlddata;    /* Reset stream when no data left. */
  223.   return FALSE;
  224. }
  225.  
  226.  
  227. /* Given longitude and latitude values on a globe, return the window        */
  228. /* coordinates corresponding to them. In other words, project the globe     */
  229. /* onto the view plane, and return where our coordinates got projected to,  */
  230. /* as well as whether our location is hidden on the back side of the globe. */
  231.  
  232. int GlobeCalc(x1, y1, u, v, cx, cy, rx, ry, deg)
  233. real x1, y1;
  234. int *u, *v, cx, cy, rx, ry, deg;
  235. {
  236.   real j, siny1;
  237.  
  238.   /* Compute coordinates for a general globe invoked with -XG switch. */
  239.  
  240.   if (modex == MODEG) {
  241.     x1 = Mod(x1+(real)deg);    /* Shift by current globe rotation value. */
  242.     if (tilt != 0.0) {
  243.       x1 = DTOR(x1); y1 = DTOR(DEGQUAD-y1);       /* Do another coordinate  */
  244.       CoorXform(&x1, &y1, tilt / DEGRAD);         /* shift if the globe's   */
  245.       x1 = Mod(RTOD(x1)); y1 = DEGQUAD-RTOD(y1);  /* equator is tilted any. */
  246.     }
  247.     *v = cy + (int) ((real)ry*-COSD(y1)-ROUND);
  248.     *u = cx + (int) ((real)rx*-COSD(x1)*SIND(y1)-ROUND);
  249.     return x1 > DEGHALF;
  250.   }
  251.  
  252.   /* Compute coordinates for a polar globe invoked with -XP switch. */
  253.  
  254.   siny1 = SIND(y1);
  255.   j = xbonus ? DEGQUAD+x1+deg : 270.0-x1-deg;
  256.   *v = cy + (int) (siny1*(real)ry*SIND(j)-ROUND);
  257.   *u = cx + (int) (siny1*(real)rx*COSD(j)-ROUND);
  258.   return xbonus ? y1 < DEGQUAD : y1 > DEGQUAD;
  259. }
  260.  
  261.  
  262. /* Draw a globe in the window, based on the specified rotational and tilt  */
  263. /* values. In addition, we may draw in each planet at its zenith position. */
  264.  
  265. void DrawGlobe(deg)
  266. int deg;
  267. {
  268.   char *nam, *loc, *lin, d;
  269.   int X[TOTAL+1], Y[TOTAL+1], M[TOTAL+1], N[TOTAL+1],
  270.     cx = chartx/2, cy = charty/2, rx, ry, lon, lat, unit = 12*SCALE,
  271.     x, y, m, n, u, v, i, J, k, l, o;
  272.   real planet1[TOTAL+1], planet2[TOTAL+1], x1, y1, j;
  273.   colpal c;
  274.  
  275.   rx = cx-1; ry = cy-1;
  276.  
  277.   /* Loop through each coastline string, drawing visible parts on the globe. */
  278.  
  279.   while (ReadWorldData(&nam, &loc, &lin)) {
  280.     i = nam[0]-'0';
  281.     c = (modex == MODEG && xbonus) ? gray :
  282.       (i ? rainbowcolor[i] : maincolor[6]);
  283.     DrawColor(c);
  284.  
  285.     /* Get starting longitude and latitude of current coastline piece. */
  286.  
  287.     lon = (loc[0] == '+' ? 1 : -1)*
  288.       ((loc[1]-'0')*100 + (loc[2]-'0')*10 + (loc[3]-'0'));
  289.     lat = (loc[4] == '+' ? 1 : -1)*((loc[5]-'0')*10 + (loc[6]-'0'));
  290.     x = 180-lon;
  291.     y = 90-lat;
  292.     GlobeCalc((real) x, (real) y, &m, &n, cx, cy, rx, ry, deg);
  293.  
  294.     /* Go down the coastline piece, drawing each visible segment on globe. */
  295.  
  296.     o = (tilt == 0.0 && modex != MODEP);
  297.     k = l = TRUE;
  298.     while (d = *lin++) {
  299.       if (d == 'L' || d == 'H' || d == 'G')
  300.         x--;
  301.       else if (d == 'R' || d == 'E' || d == 'F')
  302.         x++;
  303.       if (d == 'U' || d == 'H' || d == 'E')
  304.         y--;
  305.       else if (d == 'D' || d == 'G' || d == 'F')
  306.         y++;
  307.       if (x > 359)
  308.         x = 0;
  309.       else if (x < 0)
  310.         x = 359;
  311.       if (o) {
  312.         k = x+deg;
  313.         if (k > 359)
  314.           k -= DEGR;
  315.         k = (k <= 180);
  316.       }
  317.       if (k && !GlobeCalc((real) x, (real) y, &u, &v, cx, cy, rx, ry, deg)) {
  318.         if (l)
  319.           DrawLine(m, n, u, v);
  320.         m = u; n = v;
  321.         l = TRUE;
  322.       } else
  323.         l = FALSE;
  324.     }
  325.   }
  326.  
  327.   DrawColor(on);
  328.   DrawEllipse(0, 0, chartx-1, charty-1);
  329.  
  330.   /* Now, only if we are in bonus chart mode, draw each planet at its */
  331.   /* zenith location on the globe, assuming that location is visible. */
  332.  
  333.   if (modex != MODEG || !xbonus)
  334.     return;
  335.   j = Lon;
  336.   if (j < 0.0)
  337.     j += DEGREES;
  338.   for (i = 1; i <= total; i++) {
  339.     planet1[i] = DTOR(planet[i]);
  340.     planet2[i] = DTOR(planetalt[i]);
  341.     EclToEqu(&planet1[i], &planet2[i]);    /* Calculate zenith long. & lat. */
  342.   }
  343.   for (i = 1; i <= total; i++) if (Proper(i)) {
  344.     x1 = planet1[_MC]-planet1[i];
  345.     if (x1 < 0.0)
  346.       x1 += 2.0*PI;
  347.     if (x1 > PI)
  348.       x1 -= 2.0*PI;
  349.     x1 = Mod(DEGHALF-j-RTOD(x1));
  350.     y1 = DEGQUAD-RTOD(planet2[i]);
  351.     X[i] = GlobeCalc(x1, y1, &u, &v, cx, cy, rx, ry, deg) ? -1000 : u;
  352.     Y[i] = v; M[i] = X[i]; N[i] = Y[i]+unit/2;
  353.   }
  354.  
  355.   /* Now that we have the coordinates of each object, figure out where to    */
  356.   /* draw the glyphs. Again, we try not to draw glyphs on top of each other. */
  357.  
  358.   for (i = 1; i <= total; i++) if (Proper(i)) {
  359.     k = l = chartx+charty;
  360.  
  361.     /* For each planet, we draw the glyph either right over or right under */
  362.     /* the actual zenith location point. So, find out the closest distance */
  363.     /* of any other planet assuming we place ours at both possibilities.   */
  364.  
  365.     for (J = 1; J < i; J++) if (Proper(J)) {
  366.       k = MIN(k, abs(M[i]-M[J])+abs(N[i]-N[J]));
  367.       l = MIN(l, abs(M[i]-M[J])+abs(N[i]-unit-N[J]));
  368.     }
  369.  
  370.     /* Normally, we put the glyph right below the actual point. If however   */
  371.     /* another planet is close enough to have their glyphs overlap, and the  */
  372.     /* above location is better of, then we'll draw the glyph above instead. */
  373.  
  374.     if (k < unit || l < unit)
  375.       if (k < l)
  376.         N[i] -= unit;
  377.   }
  378.   for (i = total; i >= 1; i--) if (X[i] >= 0 && Proper(i))      /* Draw the */
  379.     DrawObject(i, M[i], N[i]);                                  /* glyphs.  */
  380.   for (i = total; i >= 1; i--) if (X[i] >= 0 && Proper(i)) {
  381.     DrawColor(objectcolor[i]);
  382.     DrawSpot(X[i], Y[i]);
  383.   }
  384. }
  385.  
  386.  
  387. /* Draw one "Ley line" on the world map, based coordinates given in terms of */
  388. /* longitude and vertical fractional distance from the center of the earth.  */
  389.  
  390. void DrawLeyLine(l1, f1, l2, f2)
  391. real l1, f1, l2, f2;
  392. {
  393.   l1 = Mod(l1); l2 = Mod(l2);
  394.  
  395.   /* Convert vertical fractional distance to a corresponding coordinate. */
  396.  
  397.   f1 = DEGQUAD-ASIN(f1)/(PI/2.0)*DEGQUAD;
  398.   f2 = DEGQUAD-ASIN(f2)/(PI/2.0)*DEGQUAD;
  399.   DrawWrap((int) (l1*(real)SCALE+ROUND)+1,
  400.            (int) (f1*(real)SCALE+ROUND)+1,
  401.            (int) (l2*(real)SCALE+ROUND)+1,
  402.            (int) (f2*(real)SCALE+ROUND)+1, 1, chartx-2);
  403. }
  404.  
  405.  
  406. /* Draw the main set of planetary Ley lines on the map of the world. This */
  407. /* consists of drawing an icosahedron and then a dodecahedron lattice.    */
  408.  
  409. void DrawLeyLines(deg)
  410. int deg;
  411. {
  412.   real off = (real)deg, phi, h, h1, h2, r, i;
  413.  
  414.   phi = (sqrt(5.0)+1.0)/2.0;                     /* Icosahedron constants. */
  415.   h = 1.0/(phi*2.0-1.0);
  416.   DrawColor(aspectcolor[10]);
  417.   for (i = off; i < DEGREES+off; i += 72.0) {    /* Draw icosahedron edges. */
  418.     DrawLeyLine(i, h, i+72.0, h);
  419.     DrawLeyLine(i-36.0, -h, i+36.0, -h);
  420.     DrawLeyLine(i, h, i, 1.0);
  421.     DrawLeyLine(i+36.0, -h, i+36.0, -1.0);
  422.     DrawLeyLine(i, h, i+36.0, -h);
  423.     DrawLeyLine(i, h, i-36.0, -h);
  424.   }
  425.   r = 1.0/sqrt(3.0)/phi/cos(DTOR(54.0));         /* Dodecahedron constants. */
  426.   h2 = sqrt(1.0-r*r); h1 = h2/(phi*2.0+1.0);
  427.   DrawColor(aspectcolor[13]);
  428.   for (i = off; i < DEGREES+off; i += 72.0) {    /* Draw docecahedron edges. */
  429.     DrawLeyLine(i-36.0, h2, i+36.0, h2);
  430.     DrawLeyLine(i, -h2, i+72.0, -h2);
  431.     DrawLeyLine(i+36.0, h2, i+36.0, h1);
  432.     DrawLeyLine(i, -h2, i, -h1);
  433.     DrawLeyLine(i+36.0, h1, i+72.0, -h1);
  434.     DrawLeyLine(i+36.0, h1, i, -h1);
  435.   }
  436. }
  437.  
  438.  
  439. /* Draw a map of the world on the screen. This is similar to drawing the  */
  440. /* globe, but is simplified because this is just a rectangular image, and */
  441. /* the window coordinates are proportional to the longitude and latitude. */
  442.  
  443. void DrawWorld(deg)
  444. int deg;
  445. {
  446.   char *nam, *loc, *lin, d;
  447.   int lon, lat, x, y, xold, yold, i;
  448.   colpal c;
  449.  
  450.   /* Loop through each coastline string, drawing it on the world map. */
  451.  
  452.   while (ReadWorldData(&nam, &loc, &lin)) {
  453.     i = nam[0]-'0';
  454.     c = modex == MODEL ? on : (i ? rainbowcolor[i] : maincolor[6]);
  455.  
  456.     /* Get starting longitude and latitude of current coastline piece. */
  457.  
  458.     lon = (loc[0] == '+' ? 1 : -1)*
  459.       ((loc[1]-'0')*100 + (loc[2]-'0')*10 + (loc[3]-'0'));
  460.     lat = (loc[4] == '+' ? 1 : -1)*((loc[5]-'0')*10 + (loc[6]-'0'));
  461.     xold = x = (int) Mod((real)(181-lon+deg));
  462.     yold = y = 91-lat;
  463.  
  464.     /* Go down the coastline piece, drawing each segment on world map. */
  465.  
  466.     for (i = 0; d = lin[i]; i++) {
  467.       if (d == 'L' || d == 'H' || d == 'G')
  468.         x--;
  469.       else if (d == 'R' || d == 'E' || d == 'F')
  470.         x++;
  471.       if (d == 'U' || d == 'H' || d == 'E')
  472.         y--;
  473.       else if (d == 'D' || d == 'G' || d == 'F')
  474.         y++;
  475.       if (x > DEGR) {
  476.         x = 1;
  477.         xold = 0;
  478.       }
  479.  
  480.       /* If we are doing a Mollewide map projection, then transform the */
  481.       /* coordinates appropriately before drawing the segment.          */
  482.  
  483.       DrawColor(c);
  484.       if ((exdisplay & DASHXW0) > 0 && modex != MODEL)
  485.         DrawLine((180+(xold-180)*
  486.           (int)sqrt((real)(32400-4*(yold-91)*(yold-91)))/180)*SCALE,
  487.           yold*SCALE,
  488.           (180+(x-180)*(int)sqrt((real)(32400-4*(y-91)*(y-91)))/180)*SCALE,
  489.           y*SCALE);
  490.       else
  491.         DrawLine(xold*SCALE, yold*SCALE, x*SCALE, y*SCALE);
  492.       if (x < 1)
  493.         x = DEGR;
  494.       xold = x; yold = y;
  495.     }
  496.   }
  497.  
  498.   /* Again, if we are doing the non-rectangular Mollewide map projection, */
  499.   /* draw the outline of the globe/map itself.                            */
  500.  
  501.   if ((exdisplay & DASHXW0) > 0 && modex != MODEL) {
  502.     if (!xbonus) {
  503.       DrawColor(on);
  504.       for (xold = 0, y = -89; y <= 90; y++, xold = x)
  505.         for (x = (int)(sqrt((real)(32400-4*y*y))+ROUND), i = -1; i < 2; i += 2)
  506.           DrawLine((180+i*xold)*SCALE, (90+y)*SCALE,
  507.             (180+i*x)*SCALE, (91+y)*SCALE);
  508.     }
  509.   }
  510. }
  511.  
  512.  
  513. /* Given a zodiac degree, adjust it if need be to account for the expanding */
  514. /* and compacting of parts the zodiac that happen when we display a graphic */
  515. /* wheel chart such that all the houses appear the same size.               */
  516.  
  517. real XHousePlaceIn(deg)
  518. real deg;
  519. {
  520.   int in;
  521.  
  522.   if (modex == MODEv)    /* We only adjust for the -w -X combination. */
  523.     return deg;
  524.   in = HousePlaceIn(deg);
  525.   return Mod(STOZ(in)+MinDistance(house[in], deg)/
  526.     MinDistance(house[in], house[Mod12(in+1)])*30.0);
  527. }
  528.  
  529.  
  530. /*
  531. ******************************************************************************
  532. ** Multiple Chart Graphics Subprograms.
  533. ******************************************************************************
  534. */
  535.  
  536. /* Draw another wheel chart; however, this time we have two rings of planets */
  537. /* because we are doing a relationship chart between two sets of data. This  */
  538. /* chart is obtained when the -r0 is combined with the -X switch.            */
  539.  
  540. void XChartWheelRelation()
  541. {
  542.   real xsign[SIGNS+1], xhouse1[SIGNS+1], xplanet1[TOTAL+1], xplanet2[TOTAL+1],
  543.     symbol[TOTAL+1];
  544.   int cx, cy, i, j;
  545.   real asc, unitx, unity, px, py, temp;
  546.  
  547.   /* Set up variables and temporarily automatically decrease the horizontal */
  548.   /* chart size to leave room for the sidebar if that mode is in effect.    */
  549.  
  550.   if (xtext && !(exdisplay & DASHv0))
  551.     chartx -= SIDET;
  552.   cx = chartx/2 - 1; cy = charty/2 - 1;
  553.   unitx = (real)cx; unity = (real)cy;
  554.   asc = xeast ? planet1[abs(xeast)]+90*(xeast < 0) : house1[1];
  555.   InitCircle();
  556.  
  557.   /* Fill out arrays with the degree of each object, cusp, and sign glyph. */
  558.  
  559.   if (modex == MODEv) {
  560.     for (i = 1; i <= SIGNS; i++)
  561.       xhouse1[i] = PZ(house1[i]);
  562.   } else {
  563.     asc -= house1[1];
  564.     for (i = 1; i <= SIGNS; i++)
  565.       xhouse1[i] = PZ(STOZ(i));
  566.   }
  567.   for (i = 1; i <= SIGNS; i++)
  568.     xsign[i] = PZ(XHousePlaceIn(STOZ(i)));
  569.   for (i = 1; i <= total; i++)
  570.     xplanet1[i] = PZ(XHousePlaceIn(planet1[i]));
  571.   for (i = 1; i <= total; i++)
  572.     xplanet2[i] = PZ(XHousePlaceIn(planet2[i]));
  573.  
  574.   /* Draw the horizon and meridian lines across whole chart, and draw the */
  575.   /* zodiac and house rings, exactly like before. We are drawing only the */
  576.   /* houses of one of the two charts in the relationship, however.        */
  577.  
  578.   DrawColor(hilite);
  579.   DrawDash(cx+POINT(unitx, 0.99, PX(xhouse1[1])),
  580.            cy+POINT(unity, 0.99, PY(xhouse1[1])),
  581.            cx+POINT(unitx, 0.99, PX(xhouse1[7])),
  582.            cy+POINT(unity, 0.99, PY(xhouse1[7])), !xcolor);
  583.   DrawDash(cx+POINT(unitx, 0.99, PX(xhouse1[10])),
  584.            cy+POINT(unity, 0.99, PY(xhouse1[10])),
  585.            cx+POINT(unitx, 0.99, PX(xhouse1[4])),
  586.            cy+POINT(unity, 0.99, PY(xhouse1[4])), !xcolor);
  587.   for (i = 0; i < DEGR; i += 5-(xcolor || psfile || metafile)*4) {
  588.     temp = PZ(XHousePlaceIn((real)i));
  589.     px = PX(temp); py = PY(temp);
  590.     DrawColor(i%5 ? gray : on);
  591.     DrawDash(cx+POINT(unitx, 0.78, px), cy+POINT(unity, 0.78, py),
  592.       cx+POINT(unitx, 0.82, px), cy+POINT(unity, 0.82, py),
  593.       ((psfile || metafile) && i%5)*2);
  594.   }
  595.  
  596.   DrawColor(on);
  597.   DrawCircle(cx, cy, (int)(unitx*0.95+ROUND), (int)(unity*0.95+ROUND));
  598.   DrawCircle(cx, cy, (int)(unitx*0.82+ROUND), (int)(unity*0.82+ROUND));
  599.   DrawCircle(cx, cy, (int)(unitx*0.78+ROUND), (int)(unity*0.78+ROUND));
  600.   DrawCircle(cx, cy, (int)(unitx*0.70+ROUND), (int)(unity*0.70+ROUND));
  601.  
  602.   for (i = 1; i <= SIGNS; i++) {
  603.     temp = xsign[i];
  604.     DrawColor(on);
  605.     DrawLine(cx+POINT(unitx, 0.95, PX(temp)),
  606.       cy+POINT(unity, 0.95, PY(temp)),
  607.       cx+POINT(unitx, 0.82, PX(temp)),
  608.       cy+POINT(unity, 0.82, PY(temp)));
  609.     DrawLine(cx+POINT(unitx, 0.78, PX(xhouse1[i])),
  610.       cy+POINT(unity, 0.78, PY(xhouse1[i])),
  611.       cx+POINT(unitx, 0.70, PX(xhouse1[i])),
  612.       cy+POINT(unity, 0.70, PY(xhouse1[i])));
  613.     if (xcolor && i%3 != 1) {
  614.       DrawColor(gray);
  615.       DrawDash(cx, cy, cx+POINT(unitx, 0.70, PX(xhouse1[i])),
  616.         cy+POINT(unity, 0.70, PY(xhouse1[i])), 1);
  617.     }
  618.     temp = Midpoint(temp, xsign[Mod12(i+1)]);
  619.     DrawColor(signcolor(i));
  620.     DrawSign(i, cx+POINT(unitx, 0.885, PX(temp)),
  621.       cy+POINT(unity, 0.885, PY(temp)));
  622.     temp = Midpoint(xhouse1[i], xhouse1[Mod12(i+1)]);
  623.     DrawHouse(i, cx+POINT(unitx, 0.74, PX(temp)),
  624.       cy+POINT(unity, 0.74, PY(temp)));
  625.   }
  626.  
  627.   /* Draw the outer ring of planets (based on the planets in the chart     */
  628.   /* which the houses do not reflect - the houses belong to the inner ring */
  629.   /* below). Draw each glyph, a line from it to its actual position point  */
  630.   /* in the outer ring, and then draw another line from this point to a    */
  631.   /* another dot at the same position in the inner ring as well.           */
  632.  
  633.   for (i = 1; i <= total; i++)
  634.     symbol[i] = xplanet2[i];
  635.   FillSymbolRing(symbol);
  636.   for (i = 1; i <= total; i++) if (Proper(i)) {
  637.     if (xlabel) {
  638.       temp = symbol[i];
  639.       DrawColor(ret2[i] < 0.0 ? gray : on);
  640.       DrawDash(cx+POINT(unitx, 0.58, PX(xplanet2[i])),
  641.         cy+POINT(unity, 0.58, PY(xplanet2[i])),
  642.         cx+POINT(unitx, 0.61, PX(temp)),
  643.         cy+POINT(unity, 0.61, PY(temp)),
  644.         (ret2[i] < 0.0 ? 1 : 0) - xcolor);
  645.       DrawObject(i, cx+POINT(unitx, 0.65, PX(temp)),
  646.         cy+POINT(unity, 0.65, PY(temp)));
  647.     }
  648.     DrawColor(objectcolor[i]);
  649.     DrawPoint(cx+POINT(unitx, 0.56, PX(xplanet2[i])),
  650.       cy+POINT(unity, 0.56, PY(xplanet2[i])));
  651.     DrawPoint(cx+POINT(unitx, 0.43, PX(xplanet2[i])),
  652.       cy+POINT(unity, 0.43, PY(xplanet2[i])));
  653.     DrawColor(ret2[i] < 0.0 ? gray : on);
  654.     DrawDash(cx+POINT(unitx, 0.45, PX(xplanet2[i])),
  655.       cy+POINT(unity, 0.45, PY(xplanet2[i])),
  656.       cx+POINT(unitx, 0.54, PX(xplanet2[i])),
  657.       cy+POINT(unity, 0.54, PY(xplanet2[i])), 2-xcolor);
  658.   }
  659.  
  660.   /* Now draw the inner ring of planets. If it weren't for the outer ring,  */
  661.   /* this would be just like the standard non-relationship wheel chart with */
  662.   /* only one set of planets. Again, draw glyph, and a line to true point.  */
  663.  
  664.   for (i = 1; i <= total; i++) {
  665.     symbol[i] = xplanet1[i];
  666.   }
  667.   FillSymbolRing(symbol);
  668.   for (i = 1; i <= total; i++) if (Proper(i)) {
  669.     if (xlabel) {
  670.       temp = symbol[i];
  671.       DrawColor(ret1[i] < 0.0 ? gray : on);
  672.       DrawDash(cx+POINT(unitx, 0.45, PX(xplanet1[i])),
  673.         cy+POINT(unity, 0.45, PY(xplanet1[i])),
  674.         cx+POINT(unitx, 0.48, PX(temp)),
  675.         cy+POINT(unity, 0.48, PY(temp)),
  676.         (ret1[i] < 0.0 ? 1 : 0) - xcolor);
  677.       DrawObject(i, cx+POINT(unitx, 0.52, PX(temp)),
  678.         cy+POINT(unity, 0.52, PY(temp)));
  679.     } else
  680.       DrawColor(objectcolor[i]);
  681.     DrawPoint(cx+POINT(unitx, 0.43, PX(xplanet1[i])),
  682.       cy+POINT(unity, 0.43, PY(xplanet1[i])));
  683.   }
  684.  
  685.   /* Draw lines connecting planets between the two charts that have aspects. */
  686.  
  687.   if (!xbonus) {                  /* Don't draw aspects in bonus mode. */
  688.     CreateGridRelation(FALSE);
  689.     for (j = total; j >= 1; j--)
  690.       for (i = total; i >= 1; i--)
  691.         if (grid->n[i][j] && Proper(i) && Proper(j)) {
  692.           DrawColor(aspectcolor[grid->n[i][j]]);
  693.           DrawDash(cx+POINT(unitx, 0.41, PX(xplanet1[j])),
  694.             cy+POINT(unity, 0.41, PY(xplanet1[j])),
  695.             cx+POINT(unitx, 0.41, PX(xplanet2[i])),
  696.             cy+POINT(unity, 0.41, PY(xplanet2[i])),
  697.             abs(grid->v[i][j]/60/2));
  698.         }
  699.   }
  700.  
  701.   /* Go draw sidebar with chart information and positions if need be. */
  702.  
  703.   DrawInfo();
  704. }
  705.  
  706.  
  707. /* Draw an aspect (or midpoint) grid in the window, between the planets in  */
  708. /* two different charts, with the planets labeled at the top and side. This */
  709. /* chart is done when the -g switch is combined with the -r0 and -X switch. */
  710. /* Like above, the chart always has a (definable) fixed number of cells.    */
  711.  
  712. void XChartGridRelation()
  713. {
  714.   char string[STRING];
  715.   int unit, siz, x, y, i, j, k, l;
  716.   colpal c;
  717.  
  718.   unit = CELLSIZE*SCALE; siz = (gridobjects+1)*unit;
  719.   CreateGridRelation(xbonus != (exdisplay & DASHg0) > 0);
  720.   for (y = 0, j = -1; y <= gridobjects; y++) {
  721.     do {
  722.       j++;
  723.     } while (ignore[j] && j <= total);
  724.     DrawColor(gray);
  725.     DrawDash(0, (y+1)*unit, siz, (y+1)*unit, !xcolor);
  726.     DrawDash((y+1)*unit, 0, (y+1)*unit, siz, !xcolor);
  727.     DrawColor(hilite);
  728.     DrawEdge(0, y*unit, unit, (y+1)*unit);
  729.     DrawEdge(y*unit, 0, (y+1)*unit, unit);
  730.     if (j <= total) for (x = 0, i = -1; x <= gridobjects; x++) {
  731.       do {
  732.         i++;
  733.       } while (ignore[i] && i <= total);
  734.  
  735.       /* Again, we are looping through each cell in each row and column. */
  736.  
  737.       if (i <= total) {
  738.         turtlex = x*unit+unit/2;
  739.         turtley = y*unit+unit/2 - (SCALE/scalet > 2 ? 5*scalet : 0);
  740.  
  741.         /* If current cell is on top row or left hand column, draw glyph */
  742.         /* of planet owning the particular row or column in question.    */
  743.  
  744.         if (y == 0 || x == 0) {
  745.           if (x+y > 0)
  746.             DrawObject(j == 0 ? i : j, turtlex, turtley);
  747.         } else {
  748.  
  749.         /* Otherwise, draw glyph of aspect in effect, or glyph of */
  750.         /* sign of midpoint, between the two planets in question. */
  751.  
  752.           if (xbonus == (exdisplay & DASHg0) > 0) {
  753.             DrawColor(c = aspectcolor[grid->n[i][j]]);
  754.             DrawAspect(grid->n[i][j], turtlex, turtley);
  755.           } else {
  756.             DrawColor(c = signcolor(grid->n[i][j]));
  757.             DrawSign(grid->n[i][j], turtlex, turtley);
  758.           }
  759.         }
  760.  
  761.         /* Again, when scale size is 300, print some text in current cell: */
  762.  
  763.         if (SCALE/scalet > 2 && xlabel) {
  764.  
  765.           /* For top and left edges, print sign and degree of the planet. */
  766.  
  767.           if (y == 0 || x == 0) {
  768.             if (x+y > 0) {
  769.               k = ZTOS(y == 0 ? planet2[i] : planet1[j]);
  770.               l = (int)((y == 0 ? planet2[i] : planet1[j])-STOZ(k));
  771.               c = signcolor(k);
  772.               sprintf(string, "%c%c%c %02d", SIGNAM(k), l);
  773.  
  774.               /* For extreme upper left corner, print some little arrows */
  775.               /* pointing out chart1's planets and chart2's planets.     */
  776.  
  777.             } else {
  778.               c = hilite;
  779.               sprintf(string, "1v 2->");
  780.             }
  781.           } else {
  782.             k = abs(grid->v[i][j]);
  783.  
  784.             /* For aspect cells, print the orb in degrees and minutes. */
  785.  
  786.             if (xbonus == (exdisplay & DASHg0) > 0)
  787.               if (grid->n[i][j])
  788.                 sprintf(string, "%c%d %02d'", k != grid->v[i][j] ? (exdisplay &
  789.                   DASHga ? 'a' : '-') : (exdisplay & DASHga ? 's' : '+'),
  790.                   k/60, k%60);
  791.               else
  792.                 sprintf(string, "");
  793.  
  794.             /* For midpoint cells, print degree and minute. */
  795.  
  796.             else
  797.               sprintf(string, "%2d %02d'", k/60, k%60);
  798.           }
  799.           DrawColor(c);
  800.           DrawText(string, x*unit+unit/2, (y+1)*unit-3*scalet, TRUE);
  801.         }
  802.       }
  803.     }
  804.   }
  805. }
  806.  
  807.  
  808. #ifdef BIORHYTHM
  809. #define BIODAYS 14     /* Number of days to include in each half of chart. */
  810.  
  811. /* Draw a graphic biorhythm chart on the screen, as is done when the -rb    */
  812. /* switch is combined with -X. This is technically a relationship chart in  */
  813. /* that biorhythm status is determined by a natal chart time at another     */
  814. /* later time. For the day in question, and for two weeks before and after, */
  815. /* the Physical, Emotional, and Mental percentages are plotted.             */
  816.  
  817. void XChartBiorhythm()
  818. {
  819.   char string[6], *c;
  820.   real jd, r, a;
  821.   int x1, x2, xs, cx, y1, y2, ys, cy, i, j, k, x, y, x0, y0;
  822.  
  823.   k = FONTX*6*scalet;
  824.   x1 = k; x2 = chartx-k; xs = x2-x1; cx = (x1+x2)/2;
  825.   k = CELLSIZE;
  826.   y1 = k; y2 = charty-k; ys = y2-y1; cy = (y1+y2)/2;
  827.  
  828.   /* Create a dotted day/percentage grid to graph on. */
  829.   DrawColor(gray);
  830.   DrawDash(x1, cy, x2, cy, 1);
  831.   DrawDash(cx, y1, cx, y2, 1);
  832.   for (j = -BIODAYS+1; j <= BIODAYS-1; j++) {
  833.     x = x1 + MULTDIV(xs, j+BIODAYS, BIODAYS*2);
  834.     for (k = -90; k <= 90; k += 10) {
  835.       y = y1 + MULTDIV(ys, 100+k, 200);
  836.       DrawPoint(x, y);
  837.     }
  838.   }
  839.  
  840.   /* Now actually draw the three biorhythm curves. */
  841.   for (i = 1; i <= 3; i++) {
  842.     jd = floor(JD + ROUND);
  843.     switch (i) {
  844.     case 1: r = _PHY; c = "PHYS"; DrawColor(elemcolor[_FIR]); break;
  845.     case 2: r = _EMO; c = "EMOT"; DrawColor(elemcolor[_WAT]); break;
  846.     case 3: r = _INT; c = "INTE"; DrawColor(elemcolor[_EAR]); break;
  847.     }
  848.     for (jd -= (real)BIODAYS, j = -BIODAYS; j <= BIODAYS; j++, jd += 1.0) {
  849.       a = Biorhythm(jd, r);
  850.       x = x1 + MULTDIV(xs, j+BIODAYS, BIODAYS*2);
  851.       y = y1 + (int)((real)ys * (100.0-a) / 200.0);
  852.       if (j > -BIODAYS)
  853.         DrawLine(x0, y0, x, y);
  854.       else
  855.         DrawText(c, x1/2, y+2*scalet, FALSE);
  856.       x0 = x; y0 = y;
  857.     }
  858.   }
  859.  
  860.   DrawColor(hilite);
  861.   /* Label biorhythm percentages along right vertical axis. */
  862.   for (k = -100; k <= 100; k += 10) {
  863.     sprintf(string, "%c%3d%%", k < 0 ? '-' : '+', abs(k));
  864.     y = y1 + MULTDIV(ys, 100-k, 200);
  865.     DrawText(string, (x2+chartx)/2, y+2*scalet, FALSE);
  866.   }
  867.   /* Label days on top horizontal axis. */
  868.   for (j = -BIODAYS; j <= BIODAYS; j += 2) {
  869.     x = x1 + MULTDIV(xs, j+BIODAYS, BIODAYS*2);
  870.     sprintf(string, "%c%d", j < 0 ? '-' : '+', abs(j));
  871.     DrawText(string, x, y1-2*scalet, TRUE);
  872.   }
  873.   DrawEdge(x1, y1, x2, y2);
  874. }
  875. #endif
  876.  
  877.  
  878. /* Create a chart in the window based on the current graphics chart mode. */
  879. /* This is the main dispatch routine for all of the program's graphics.   */
  880.  
  881. void XChart()
  882. {
  883.   char string[STRING];
  884.   int i, j, k;
  885.  
  886.   scalet = psfile ? PSMUL : (metafile ? METAMUL : 1);
  887.  
  888.   DrawClearScreen();
  889.   switch (modex) {
  890.   case MODEv:
  891.   case MODEw:
  892.     if (relation > DASHr0)
  893.       XChartWheel();
  894.     else
  895.       XChartWheelRelation();
  896.     break;
  897.   case MODEL:
  898.     DrawWorld(degree);     /* First draw map of world.           */
  899.     XChartAstroGraph();    /* Then draw astro-graph lines on it. */
  900.     break;
  901.   case MODEg:
  902.     if (relation > DASHr0)
  903.       XChartGrid();
  904.     else
  905.       XChartGridRelation();
  906.     break;
  907.   case MODEZ:
  908.     if (exdisplay & DASHZ0)
  909.       XChartHorizonSky();
  910.     else
  911.       XChartHorizon();
  912.     break;
  913.   case MODES:
  914.     XChartSpace();
  915.     break;
  916.   case MODEE:
  917.     XChartEphemeris();
  918.     break;
  919.   case MODEW:
  920.     DrawWorld(degree);                         /* First draw map of world. */
  921.     if (xbonus && (exdisplay & DASHXW0) == 0)  /* Then maybe Ley lines.    */
  922.       DrawLeyLines(degree);
  923.     break;
  924.   case MODEG:
  925.   case MODEP:
  926.     DrawGlobe(degree);
  927.     break;
  928. #ifdef BIORHYTHM
  929.   case MODEb:
  930.     XChartBiorhythm();
  931.     break;
  932. #endif
  933.   }
  934.  
  935.   /* Print text showing chart information at bottom of window. */
  936.  
  937.   DrawColor(hilite);
  938.   if (xtext && modex != MODEW && modex != MODEG && modex != MODEP &&
  939.     ((modex != MODEv && modex != MODEw) || (exdisplay & DASHv0) > 0)) {
  940.     if (Mon == -1)
  941.       sprintf(string, "(no time or space)");
  942.     else if (relation == DASHrc)
  943.       sprintf(string, "(composite)");
  944.     else {
  945.       i = (int) (FRACT(dabs(Tim))*100.0+ROUND/60.0);
  946.       j = (int) (FRACT(dabs(Zon))*100.0+ROUND/60.0);
  947.       k = ansi; ansi = FALSE;
  948.       sprintf(string, "%s %s (%d:%02d GMT) %s",
  949.         CharDate(Mon, Day, Yea, 2), CharTime((int)floor(Tim), i),
  950.         (int)(-Zon), j, CharLocation(Lon, Lat, 100.0));
  951.       ansi = k;
  952.     }
  953.     DrawText(string, chartx/2, charty-3*scalet, TRUE);
  954.   }
  955.  
  956.   /* Draw a border around the chart if the mode is set and appropriate. */
  957.  
  958.   if ((xborder || modex == MODEg) && modex != MODEG && modex != MODEP &&
  959.     (modex != MODEW || (exdisplay & DASHXW0) == 0))
  960.     DrawEdgeAll();
  961. }
  962. #endif /* GRAPH */
  963.  
  964. /* xoptions.c */
  965.