/* ** Astrolog (Version 4.40) File: xcharts1.c ** ** IMPORTANT NOTICE: The graphics database and chart display routines ** used in this program are Copyright (C) 1991-1995 by Walter D. Pullen ** (astara@u.washington.edu). Permission is granted to freely use and ** distribute these routines provided one doesn't sell, restrict, or ** profit from them in any way. Modification is allowed provided these ** notices remain with any altered or edited versions of the program. ** ** The main planetary calculation routines used in this program have ** been Copyrighted and the core of this program is basically a ** conversion to C of the routines created by James Neely as listed in ** Michael Erlewine's 'Manual of Computer Programming for Astrologers', ** available from Matrix Software. The copyright gives us permission to ** use the routines for personal use but not to sell them or profit from ** them in any way. ** ** The PostScript code within the core graphics routines are programmed ** and Copyright (C) 1992-1993 by Brian D. Willoughby ** (brianw@sounds.wa.com). Conditions are identical to those above. ** ** The extended accurate ephemeris databases and formulas are from the ** calculation routines in the program "Placalc" and are programmed and ** Copyright (C) 1989,1991,1993 by Astrodienst AG and Alois Treindl ** (alois@azur.ch). The use of that source code is subject to ** regulations made by Astrodienst Zurich, and the code is not in the ** public domain. This copyright notice must not be changed or removed ** by any user of this program. ** ** Initial programming 8/28,30, 9/10,13,16,20,23, 10/3,6,7, 11/7,10,21/1991. ** X Window graphics initially programmed 10/23-29/1991. ** PostScript graphics initially programmed 11/29-30/1992. ** Last code change made 1/29/1995. */ #include "astrolog.h" #ifdef GRAPH /* ****************************************************************************** ** Single Chart Graphics Routines. ****************************************************************************** */ /* Draw a wheel chart, in which the 12 signs and houses are delineated, and */ /* the planets are inserted in their proper places. This is the default */ /* graphics chart to generate, as is done when the -v or -w (or no) switches */ /* are included with -X. Draw the aspects in the middle of chart, too. */ void XChartWheel() { real xsign[cSign+1], xhouse[cSign+1], xplanet[objMax], symbol[objMax]; int cx, cy, i, j; real asc, unitx, unity, px, py, temp; /* Set up variables and temporarily automatically decrease the horizontal */ /* chart size to leave room for the sidebar if that mode is in effect. */ if (gs.fText && !us.fVelocity) gs.xWin -= xSideT; cx = gs.xWin/2 - 1; cy = gs.yWin/2 - 1; unitx = (real)cx; unity = (real)cy; asc = gs.nLeft ? planet[abs(gs.nLeft)]+90*(gs.nLeft < 0) : house[1]; /* Fill out arrays with the angular degree on the circle of where to */ /* place each object, cusp, and sign glyph based on how the chart mode. */ if (gi.nMode == gWheel) { for (i = 1; i <= cSign; i++) xhouse[i] = PZ(house[i]); } else { asc -= house[1]; for (i = 1; i <= cSign; i++) xhouse[i] = PZ(ZFromS(i)); } for (i = 1; i <= cSign; i++) xsign[i] = PZ(HousePlaceInX(ZFromS(i))); for (i = 1; i <= cObj; i++) xplanet[i] = PZ(HousePlaceInX(planet[i])); /* Draw Ascendant/Descendant and Midheaven/Nadir lines across whole chart. */ DrawColor(gi.kiLite); DrawDash(cx+POINT1(unitx, 0.99, PX(xhouse[sAri])), cy+POINT1(unity, 0.99, PY(xhouse[sAri])), cx+POINT1(unitx, 0.99, PX(xhouse[sLib])), cy+POINT1(unity, 0.99, PY(xhouse[sLib])), !gs.fColor); DrawDash(cx+POINT1(unitx, 0.99, PX(xhouse[sCap])), cy+POINT1(unity, 0.99, PY(xhouse[sCap])), cx+POINT1(unitx, 0.99, PX(xhouse[sCan])), cy+POINT1(unity, 0.99, PY(xhouse[sCan])), !gs.fColor); /* Draw small five or one degree increments around the zodiac sign ring. */ for (i = 0; i < nDegMax; i ++) { temp = PZ(HousePlaceInX((real)i)); px = PX(temp); py = PY(temp); DrawColor(i%5 ? gi.kiGray : gi.kiOn); DrawDash(cx+POINT1(unitx, 0.75, px), cy+POINT1(unity, 0.75, py), cx+POINT2(unitx, 0.80, px), cy+POINT2(unity, 0.80, py), ((!gs.fColor || gs.fPS || gs.fMeta) && i%5)*2); } /* Draw circles for the zodiac sign and house rings. */ DrawColor(gi.kiOn); DrawCircle(cx, cy, (int)(unitx*0.95+rRound), (int)(unity*0.95+rRound)); DrawCircle(cx, cy, (int)(unitx*0.80+rRound), (int)(unity*0.80+rRound)); DrawCircle(cx, cy, (int)(unitx*0.75+rRound), (int)(unity*0.75+rRound)); DrawCircle(cx, cy, (int)(unitx*0.65+rRound), (int)(unity*0.65+rRound)); /* Draw the glyphs for the signs and houses themselves. */ for (i = 1; i <= cSign; i++) { temp = xsign[i]; DrawColor(gi.kiOn); DrawLine(cx+POINT2(unitx, 0.95, PX(temp)), /* Draw lines separating */ cy+POINT2(unity, 0.95, PY(temp)), /* each sign and house */ cx+POINT1(unitx, 0.80, PX(temp)), /* from each other. */ cy+POINT1(unity, 0.80, PY(temp))); DrawLine(cx+POINT2(unitx, 0.75, PX(xhouse[i])), cy+POINT2(unity, 0.75, PY(xhouse[i])), cx+POINT1(unitx, 0.65, PX(xhouse[i])), cy+POINT1(unity, 0.65, PY(xhouse[i]))); if (gs.fColor && i%3 != 1) { /* Lines from */ DrawColor(gi.kiGray); /* each house */ DrawDash(cx, cy, cx+POINT2(unitx, 0.65, PX(xhouse[i])), /* to center */ cy+POINT2(unity, 0.65, PY(xhouse[i])), 1); /* of wheel. */ } temp = Midpoint(temp, xsign[Mod12(i+1)]); DrawColor(kSignB(i)); DrawSign(i, cx+POINT1(unitx, 0.875, PX(temp)), cy+POINT1(unity, 0.875, PY(temp))); temp = Midpoint(xhouse[i], xhouse[Mod12(i+1)]); DrawHouse(i, cx+POINT1(unitx, 0.70, PX(temp)), cy+POINT1(unity, 0.70, PY(temp))); } for (i = 1; i <= cObj; i++) /* Figure out where to put planet glyphs. */ symbol[i] = xplanet[i]; FillSymbolRing(symbol); /* For each planet, draw a small dot indicating where it is, and then */ /* a line from that point to the planet's glyph. */ for (i = cObj; i >= 1; i--) if (FProper(i)) { if (gs.fLabel) { temp = symbol[i]; DrawColor(ret[i] < 0.0 ? gi.kiGray : gi.kiOn); DrawDash(cx+POINT1(unitx, 0.52, PX(xplanet[i])), cy+POINT1(unity, 0.52, PY(xplanet[i])), cx+POINT1(unitx, 0.56, PX(temp)), cy+POINT1(unity, 0.56, PY(temp)), (ret[i] < 0.0 ? 1 : 0) - gs.fColor); DrawObject(i, cx+POINT1(unitx, 0.60, PX(temp)), cy+POINT1(unity, 0.60, PY(temp))); } else DrawColor(kObjB[i]); DrawPoint(cx+POINT1(unitx, 0.50, PX(xplanet[i])), cy+POINT1(unity, 0.50, PY(xplanet[i]))); } /* Draw lines connecting planets which have aspects between them. */ if (!gs.fAlt) { /* Don't draw aspects in bonus mode. */ if (!FCreateGrid(fFalse)) return; for (j = cObj; j >= 2; j--) for (i = j-1; i >= 1; i--) if (grid->n[i][j] && FProper(i) && FProper(j)) { DrawColor(kAspB[grid->n[i][j]]); DrawDash(cx+POINT1(unitx, 0.48, PX(xplanet[i])), cy+POINT1(unity, 0.48, PY(xplanet[i])), cx+POINT1(unitx, 0.48, PX(xplanet[j])), cy+POINT1(unity, 0.48, PY(xplanet[j])), abs(grid->v[i][j]/60/2)); } } /* Go draw sidebar with chart information and positions if need be. */ DrawInfo(); } /* Draw an astro-graph chart on a map of the world, i.e. the draw the */ /* Ascendant, Descendant, Midheaven, and Nadir lines corresponding to the */ /* time in the chart. This chart is done when the -L switch is combined */ /* with the -X switch. */ void XChartAstroGraph() { real planet1[objMax], planet2[objMax], end1[cObj*2+1], end2[cObj*2+1], symbol1[cObj*2+1], symbol2[cObj*2+1], lon = Lon, longm, x, y, z, ad, oa, am, od, dm, lat; int unit = gi.nScale, fStroke, lat1 = -60, lat2 = 75, y1, y2, xold1, xold2, i, j, k, l; /* Erase top and bottom parts of map. We don't draw the astro-graph lines */ /* above certain latitudes, and this gives us room for glyph labels, too. */ y1 = (91-lat1)*gi.nScale; y2 = (91-lat2)*gi.nScale; DrawColor(gi.kiOff); DrawBlock(0, 1, gs.xWin-1, y2-1); DrawBlock(0, gs.yWin-2, gs.xWin-1, y1+1); DrawColor(gi.kiLite); DrawDash(0, gs.yWin/2, gs.xWin-2, gs.yWin/2, 4); /* Draw equator. */ DrawColor(gi.kiOn); DrawLine(1, y2, gs.xWin-2, y2); DrawLine(1, y1, gs.xWin-2, y1); for (i = 1; i <= cObj*2; i++) end1[i] = end2[i] = -rLarge; /* Draw small hatches every 5 degrees along edges of world map. */ DrawColor(gi.kiLite); for (i = lat1; i <= lat2; i += 5) { j = (91-i)*gi.nScale; k = (2+(i%10 == 0)+2*(i%30 == 0))*gi.nScaleT; DrawLine(1, j, k, j); DrawLine(gs.xWin-2, j, gs.xWin-1-k, j); } for (i = -180; i < 180; i += 5) { j = (180-i)*gi.nScale; k = (2+(i%10 == 0)+2*(i%30 == 0)+(i%90 == 0))*gi.nScaleT; DrawLine(j, y2+1, j, y2+k); DrawLine(j, y1-1, j, y1-k); } if (us.fLatitudeCross) { DrawColor(kRainbowB[7]); i = (int)((91.0-Lat)*(real)gi.nScale); DrawLine(0, i, gs.xWin-1, i); } #ifdef MATRIX /* Calculate zenith locations of each planet. */ for (i = 1; i <= cObj; i++) if (!ignore[i]) { planet1[i] = RFromD(Tropical(planet[i])); planet2[i] = RFromD(planetalt[i]); EclToEqu(&planet1[i], &planet2[i]); } /* Draw the Midheaven lines and zenith location markings. */ if (lon < 0.0) lon += rDegMax; for (i = 1; i <= cObj; i++) if (FProper(i)) { x = RFromD(MC)-planet1[i]; if (x < 0.0) x += 2.0*rPi; if (x > rPi) x -= 2.0*rPi; z = lon+DFromR(x); if (z > rDegHalf) z -= rDegMax; j = (int)(Mod(rDegHalf-z+gs.nRot)*(real)gi.nScale); DrawColor(kElemB[eEar]); DrawLine(j, y1+unit*4, j, y2-unit*1); end2[i*2-1] = (real)j; y = DFromR(planet2[i]); k = (int)((91.0-y)*(real)gi.nScale); if (FBetween((int)y, lat1, lat2)) { DrawColor(gi.kiLite); DrawBlock(j-1, k-1, j+1, k+1); DrawColor(gi.kiOff); DrawBlock(j, k, j, k); } /* Draw Nadir lines assuming we aren't in bonus chart mode. */ if (!gs.fAlt) { j += 180*gi.nScale; if (j > gs.xWin-2) j -= (gs.xWin-2); end1[i*2-1] = (real)j; DrawColor(kElemB[eWat]); DrawLine(j, y1+unit*2, j, y2-unit*2); } } /* Now, normally, unless we are in bonus chart mode, we will go on to draw */ /* the Ascendant and Descendant lines here. */ longm = RFromD(Mod(MC+lon)); if (!gs.fAlt) for (i = 1; i <= cObj; i++) if (FProper(i)) { xold1 = xold2 = -1000; /* Hack: Normally we draw the Ascendant and Descendant line segments */ /* simultaneously. However, for the PostScript and metafile stroke */ /* graphics, this will case the file to get inordinately large due to */ /* the constant thrashing between the Asc and Desc colors. Hence for */ /* these charts only, we'll do two passes for Asc and Desc. */ fStroke = gs.fPS || gs.fMeta; for (l = 0; l <= fStroke; l++) for (lat = (real)lat1; lat <= (real)lat2; lat += 1.0/(real)(gi.nScale/gi.nScaleT)) { /* First compute and draw the current segment of Ascendant line. */ j = (int)((91.0-lat)*(real)gi.nScale); ad = RTan(planet2[i])*RTan(RFromD(lat)); if (ad*ad > 1.0) ad = rLarge; else { ad = RAsin(ad); oa = planet1[i]-ad; if (oa < 0.0) oa += 2.0*rPi; am = oa-rPiHalf; if (am < 0.0) am += 2.0*rPi; z = longm-am; if (z < 0.0) z += 2.0*rPi; if (z > rPi) z -= 2.0*rPi; z = DFromR(z); k = (int)(Mod(rDegHalf-z+gs.nRot)*(real)gi.nScale); if (!fStroke || !l) { DrawColor(kElemB[eFir]); DrawWrap(xold1, j+gi.nScaleT, k, j, 1, gs.xWin-2); if (lat == (real)lat1) { /* Line segment */ DrawLine(k, y1, k, y1+unit*4); /* pointing to */ end2[i*2] = (real)k; /* Ascendant. */ } } else if (lat == (real)lat1) end2[i*2] = (real)k; xold1 = k; } /* The curving Ascendant and Descendant lines actually touch each at */ /* low latitudes. Sometimes when we start out, a particular planet's */ /* lines haven't appeared yet, i.e. we are scanning at a latitude */ /* where our planet's lines don't exist. If this is the case, then */ /* when they finally do start, draw a thin horizontal line connecting */ /* the Ascendant and Descendant lines so they don't just start in */ /* space. Note that these connected lines aren't labeled with glyphs. */ if (ad == rLarge) { if (xold1 >= 0) { if (!fStroke || !l) { DrawColor(gi.kiGray); DrawWrap(xold1, j+1, xold2, j+1, 1, gs.xWin-2); } lat = rDegQuad; } } else { /* Then compute and draw corresponding segment of Descendant line. */ od = planet1[i]+ad; dm = od+rPiHalf; z = longm-dm; if (z < 0.0) z += 2.0*rPi; if (z > rPi) z -= 2.0*rPi; z = DFromR(z); k = (int)(Mod(rDegHalf-z+gs.nRot)*(real)gi.nScale); if (xold2 < 0 && lat > (real)lat1 && (!fStroke || l)) { DrawColor(gi.kiGray); DrawWrap(xold1, j, k, j, 1, gs.xWin-2); } if (!fStroke || l) { DrawColor(kElemB[eAir]); DrawWrap(xold2, j+gi.nScaleT, k, j, 1, gs.xWin-2); if (lat == (real)lat1) /* Line segment */ DrawLine(k, y1, k, y1+unit*2); /* pointing to */ } /* Descendant. */ xold2 = k; } } #endif /* MATRIX */ /* Draw segments pointing to top of Ascendant and Descendant lines. */ if (ad != rLarge) { DrawColor(kElemB[eFir]); DrawLine(xold1, y2, xold1, y2-unit*1); DrawColor(kElemB[eAir]); DrawLine(k, y2, k, y2-unit*2); end1[i*2] = (real)k; } } DrawColor(kMainB[8]); i = (int)((181.0-Lon)*(real)gi.nScale); j = (int)((91.0-Lat)*(real)gi.nScale); if (us.fLatitudeCross) DrawSpot(i, j); else DrawPoint(i, j); /* Determine where to draw the planet glyphs. We have four sets of each */ /* planet - each planet's glyph appearing in the chart up to four times - */ /* one for each type of line. The Midheaven and Ascendant lines are always */ /* labeled at the bottom of the chart, while the Nadir and Midheaven lines */ /* at the top. Therefore we need to place two sets of glyphs, twice. */ for (i = 1; i <= cObj*2; i++) { symbol1[i] = end1[i]; symbol2[i] = end2[i]; } FillSymbolLine(symbol1); FillSymbolLine(symbol2); /* Now actually draw the planet glyphs. */ for (i = 1; i <= cObj*2; i++) { j = (i+1)/2; if (FProper(j)) { if ((gi.xTurtle = (int)symbol1[i]) > 0 && gs.fLabel) { DrawColor(ret[j] < 0.0 ? gi.kiGray : gi.kiOn); DrawDash((int)end1[i], y2-unit*2, (int)symbol1[i], y2-unit*4, (ret[i] < 0.0 ? 1 : 0) - gs.fColor); DrawObject(j, gi.xTurtle, y2-unit*10); } if ((gi.xTurtle = (int)symbol2[i]) > 0) { DrawColor(ret[j] < 0.0 ? gi.kiGray : gi.kiOn); DrawDash((int)end2[i], y1+unit*4, (int)symbol2[i], y1+unit*8, (ret[i] < 0.0 ? 1 : 0) - gs.fColor); DrawObject(j, gi.xTurtle, y1+unit*14); DrawTurtle(szDrawObject[i & 1 ? oMC : oAsc], (int)symbol2[i], y1+unit*24-gi.nScaleT); } } } } /* Draw an aspect and midpoint grid in the window, with planets labeled down */ /* the diagonal. This chart is done when the -g switch is combined with the */ /* -X switch. The chart always has a certain number of cells; hence based */ /* how the restrictions are set up, there may be blank columns and rows, */ /* or else only the first number of unrestricted objects will be included. */ void XChartGrid() { char sz[cchSzDef]; int unit, siz, x, y, i, j, k; KI c; unit = CELLSIZE*gi.nScale; siz = gs.nGridCell*unit; if (!FCreateGrid(gs.fAlt)) return; /* Loop through each cell in each row and column of grid. */ for (y = 1, j = 0; y <= gs.nGridCell; y++) { do { j++; } while (ignore[j] && j <= cObj); DrawColor(gi.kiGray); DrawDash(0, y*unit, siz, y*unit, !gs.fColor); DrawDash(y*unit, 0, y*unit, siz, !gs.fColor); if (j <= cObj) for (x = 1, i = 0; x <= gs.nGridCell; x++) { do { i++; } while (ignore[i] && i <= cObj); if (i <= cObj) { gi.xTurtle = x*unit-unit/2; gi.yTurtle = y*unit-unit/2 - (gi.nScale/gi.nScaleT > 2 ? 5*gi.nScaleT : 0); k = grid->n[i][j]; /* If this is an aspect cell, draw glyph of aspect in effect. */ if (gs.fAlt ? x > y : x < y) { if (k) { DrawColor(c = kAspB[k]); DrawAspect(k, gi.xTurtle, gi.yTurtle); } /* If this is a midpoint cell, draw glyph of sign of midpoint. */ } else if (gs.fAlt ? x < y : x > y) { DrawColor(c = kSignB(grid->n[i][j])); DrawSign(grid->n[i][j], gi.xTurtle, gi.yTurtle); /* For cells on main diagonal, draw glyph of planet. */ } else { DrawColor(gi.kiLite); DrawEdge((y-1)*unit, (y-1)*unit, y*unit, y*unit); DrawObject(i, gi.xTurtle, gi.yTurtle); } /* When the scale size is 300+, we can print text in each cell: */ if (gi.nScale/gi.nScaleT > 2 && gs.fLabel) { k = abs(grid->v[i][j]); /* For the aspect portion, print the orb in degrees and minutes. */ if (gs.fAlt ? x > y : x < y) { if (grid->n[i][j]) sprintf(sz, "%c%d %02d'", k != grid->v[i][j] ? (us.fAppSep ? 'a' : '-') : (us.fAppSep ? 's' : '+'), k/60, k%60); else sprintf(sz, ""); /* For the midpoint portion, print the degrees and minutes. */ } else if (gs.fAlt ? x < y : x > y) sprintf(sz, "%2d %02d'", k/60, k%60); /* For the main diagonal, print degree and sign of each planet. */ else { c = kSignB(grid->n[i][j]); sprintf(sz, "%c%c%c %02d", chSig3(grid->n[i][j]), k); } DrawColor(c); DrawSz(sz, x*unit-unit/2, y*unit-3*gi.nScaleT, dtBottom); } } } } } /* Draw the local horizon, and draw in the planets where they are at the */ /* time in question, as done when the -Z is combined with the -X switch. */ void XChartHorizon() { real lat, lonz[objMax], latz[objMax], azi[objMax], alt[objMax]; int x[objMax], y[objMax], m[objMax], n[objMax], cx, cy, unit, x1, y1, x2, y2, xs, ys, i, j, k, l; char sz[2]; unit = Max(12, 6*gi.nScale); x1 = unit; y1 = unit; x2 = gs.xWin-1-unit; y2 = gs.yWin-1-unit; unit = 12*gi.nScale; xs = x2-x1; ys = y2-y1; cx = (x1+x2)/2; cy = (y1+y2)/2; /* Make a slightly smaller rectangle within the window to draw the planets */ /* in. Make segments on all four edges marking 5 degree increments. */ DrawColor(gi.kiLite); for (i = 5; i < 180; i += 5) { j = y1+(int)((real)i*(real)ys/rDegHalf); k = (2+(i%10 == 0)+2*(i%30 == 0))*gi.nScaleT; DrawLine(x1+1, j, x1+1+k, j); DrawLine(x2-1, j, x2-1-k, j); } sz[1] = chNull; for (i = 5; i < nDegMax; i += 5) { j = x1+(int)((real)i*(real)xs/rDegMax); k = (2+(i%10 == 0)+2*(i%30 == 0))*gi.nScaleT; DrawLine(j, y1+1, j, y1+1+k); DrawLine(j, y2-1, j, y2-1-k); if (i % 90 == 0) { *sz = *szDir[i/90 & 3]; DrawSz(sz, j, y1-2*gi.nScaleT, dtBottom); } } /* Draw vertical lines dividing our rectangle into four areas. In our */ /* local space chart, the middle line represents due south, the left line */ /* due east, the right line due west, and the edges due north. A fourth */ /* horizontal line divides that which is above and below the horizon. */ DrawColor(gi.kiGray); DrawDash(cx, y1, cx, y2, 1); DrawDash((cx+x1)/2, y1, (cx+x1)/2, y2, 1); DrawDash((cx+x2)/2, y1, (cx+x2)/2, y2, 1); DrawColor(gi.kiOn); DrawEdge(x1, y1, x2, y2); DrawDash(x1, cy, x2, cy, 1); /* Calculate the local horizon coordinates of each planet. First convert */ /* zodiac position and declination to zenith longitude and latitude. */ lat = RFromD(Lat); for (i = 1; i <= cObj; i++) if (!ignore[i] || i == oMC) { lonz[i] = RFromD(Tropical(planet[i])); latz[i] = RFromD(planetalt[i]); EclToEqu(&lonz[i], &latz[i]); } for (i = 1; i <= cObj; i++) if (FProper(i)) { lonz[i] = RFromD(Mod(DFromR(lonz[oMC]-lonz[i]+rPiHalf))); EquToLocal(&lonz[i], &latz[i], rPiHalf-lat); azi[i] = rDegMax-DFromR(lonz[i]); alt[i] = DFromR(latz[i]); x[i] = x1+(int)((real)xs*(Mod(rDegQuad-azi[i]))/rDegMax+rRound); y[i] = y1+(int)((real)ys*(rDegQuad-alt[i])/rDegHalf+rRound); m[i] = x[i]; n[i] = y[i]+unit/2; } /* As in the DrawGlobe() routine, we now determine where to draw the */ /* glyphs in relation to the actual points, so that the glyphs aren't */ /* drawn on top of each other if possible. Again, we assume that we'll */ /* put the glyph right under the point, unless there would be some */ /* overlap and the above position is better off. */ for (i = 1; i <= cObj; i++) if (FProper(i)) { k = l = gs.xWin+gs.yWin; for (j = 1; j < i; j++) if (FProper(j)) { k = Min(k, abs(m[i]-m[j])+abs(n[i]-n[j])); l = Min(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j])); } if (k < unit || l < unit) if (k < l) n[i] -= unit; } for (i = cObj; i >= 1; i--) if (FProper(i)) /* Draw planet's glyph. */ DrawObject(i, m[i], n[i]); for (i = cObj; i >= 1; i--) if (FProper(i)) { DrawColor(kObjB[i]); if (!gs.fAlt || i > oNorm) DrawPoint(x[i], y[i]); /* Draw small or large dot */ else /* near glyph indicating */ DrawSpot(x[i], y[i]); /* exact local location. */ } } /* Draw the local horizon, and draw in the planets where they are at the */ /* time in question. This chart is done when the -Z0 is combined with the */ /* -X switch. This is an identical function to XChartHorizon(); however, */ /* that routine's chart is entered on the horizon and meridian. Here we */ /* center the chart around the center of the sky straight up from the */ /* local horizon, with the horizon itself being an encompassing circle. */ void XChartHorizonSky() { real lat, rx, ry, s, sqr2, lonz[objMax], latz[objMax], azi[objMax], alt[objMax]; int x[objMax], y[objMax], m[objMax], n[objMax], cx, cy, unit, x1, y1, x2, y2, xs, ys, i, j, k, l; unit = Max(12, 6*gi.nScale); x1 = unit; y1 = unit; x2 = gs.xWin-1-unit; y2 = gs.yWin-1-unit; unit = 12*gi.nScale; xs = x2-x1; ys = y2-y1; cx = (x1+x2)/2; cy = (y1+y2)/2; /* Draw a circle in window to indicate horizon line, lines dividing */ /* the window into quadrants to indicate n/s and w/e meridians, and */ /* segments on these lines and the edges marking 5 degree increments. */ sqr2 = RSqr(2.0); DrawColor(gi.kiGray); DrawDash(cx, y1, cx, y2, 1); DrawDash(x1, cy, x2, cy, 1); DrawColor(gi.kiLite); for (i = -125; i <= 125; i += 5) { k = (2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0))*gi.nScaleT; s = 1.0/(rDegQuad*sqr2); j = cy+(int)(s*ys/2*i); DrawLine(cx-k, j, cx+k, j); j = cx+(int)(s*xs/2*i); DrawLine(j, cy-k, j, cy+k); } for (i = 5; i < 55; i += 5) { k = (2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0))*gi.nScaleT; s = 1.0/(rDegHalf-rDegQuad*sqr2); j = (int)(s*ys/2*i); DrawLine(x1, y1+j, x1+k, y1+j); DrawLine(x1, y2-j, x1+k, y2-j); DrawLine(x2, y1+j, x2-k, y1+j); DrawLine(x2, y2-j, x2-k, y2-j); j = (int)(s*xs/2*i); DrawLine(x1+j, y1, x1+j, y1+k); DrawLine(x2-j, y1, x2-j, y1+k); DrawLine(x1+j, y2, x1+j, y2-k); DrawLine(x2-j, y2, x2-j, y2-k); } DrawSz("N", cx, y1-2*gi.nScaleT, dtBottom); DrawSz("E", x1/2, cy+2*gi.nScaleT, dtCent); DrawSz("W", (gs.xWin+x2)/2, cy+2*gi.nScaleT, dtCent); if (!gs.fText) DrawSz("S", cx, gs.yWin-3*gi.nScaleT, dtBottom); rx = xs/2/sqr2; ry = ys/2/sqr2; DrawColor(gi.kiOn); DrawEdge(x1, y1, x2, y2); DrawCircle(cx, cy, (int)rx, (int)ry); for (i = 0; i < nDegMax; i += 5) { k = (2+(i/10*10 == i ? 1 : 0)+(i/30*30 == i ? 2 : 0))*gi.nScaleT; DrawLine(cx+(int)((rx-k)*RCosD((real)i)), cy+(int)((ry-k)*RSinD((real)i)), cx+(int)((rx+k)*RCosD((real)i)), cy+(int)((ry+k)*RSinD((real)i))); } /* Calculate the local horizon coordinates of each planet. First convert */ /* zodiac position and declination to zenith longitude and latitude. */ lat = RFromD(Lat); for (i = 1; i <= cObj; i++) if (!ignore[i] || i == oMC) { lonz[i] = RFromD(Tropical(planet[i])); latz[i] = RFromD(planetalt[i]); EclToEqu(&lonz[i], &latz[i]); } for (i = 1; i <= cObj; i++) if (FProper(i)) { lonz[i] = RFromD(Mod(DFromR(lonz[oMC]-lonz[i]+rPiHalf))); EquToLocal(&lonz[i], &latz[i], rPiHalf-lat); azi[i] = rDegMax-DFromR(lonz[i]); alt[i] = rDegQuad-DFromR(latz[i]); s = alt[i]/rDegQuad; x[i] = cx+(int)(rx*s*RCosD(rDegHalf+azi[i])+rRound); y[i] = cy+(int)(ry*s*RSinD(rDegHalf+azi[i])+rRound); if (!FOnWin(x[i], y[i])) x[i] = -1000; m[i] = x[i]; n[i] = y[i]+unit/2; } /* As in the DrawGlobe() routine, we now determine where to draw the */ /* glyphs in relation to the actual points, so that the glyphs aren't */ /* drawn on top of each other if possible. Again, we assume that we'll */ /* put the glyph right under the point, unless there would be some */ /* overlap and the above position is better off. */ for (i = 1; i <= cObj; i++) if (FProper(i)) { k = l = gs.xWin+gs.yWin; for (j = 1; j < i; j++) if (FProper(j)) { k = Min(k, abs(m[i]-m[j])+abs(n[i]-n[j])); l = Min(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j])); } if (k < unit || l < unit) if (k < l) n[i] -= unit; } for (i = cObj; i >= 1; i--) if (m[i] >= x1 && FProper(i)) /* Draw glyph. */ DrawObject(i, m[i], n[i]); for (i = cObj; i >= 1; i--) if (x[i] >= y1 && FProper(i)) { DrawColor(kObjB[i]); if (!gs.fAlt || i > oNorm) DrawPoint(x[i], y[i]); /* Draw small or large dot */ else /* near glyph indicating */ DrawSpot(x[i], y[i]); /* exact local location. */ } } /* Draw a chart depicting an aerial view of the solar system in space, with */ /* all the planets drawn around the Sun, and the specified central planet */ /* in the middle, as done when the -S is combined with the -X switch. */ void XChartOrbit() { int x[objMax], y[objMax], m[objMax], n[objMax], cx = gs.xWin / 2, cy = gs.yWin / 2, unit, x1, y1, x2, y2, i, j, k, l; real sx, sy, sz = 30.0, xp, yp, a; unit = Max(gs.fText*12, 6*gi.nScale); x1 = unit; y1 = unit; x2 = gs.xWin-1-unit; y2 = gs.yWin-1-unit; unit = 12*gi.nScale; /* Determine the scale of the chart. For a scale size of 400+, make the */ /* graphic 1 AU in radius (just out to Earth's orbit). For 300, make */ /* the chart 6 AU in radius (enough for inner planets out to asteroid */ /* belt). For a scale of 200, make window 30 AU in radius (enough for */ /* planets out to Neptune). For scale of 100, make it 90 AU in radius */ /* (enough for all planets including the orbits of the uranians.) */ if (gi.nScale/gi.nScaleT < 2) sz = 90.0; else if (gi.nScale/gi.nScaleT == 3) sz = 6.0; else if (gi.nScale/gi.nScaleT > 3) sz = 1.0; sx = (real)(cx-x1)/sz; sy = (real)(cy-y1)/sz; for (i = 0; i <= oNorm; i++) if (FProper(i)) { /* Determine what glyph corresponds to our current planet. Normally the */ /* array indices are the same, however we have to do some swapping for */ /* non-geocentric based charts where a planet gets replaced with Earth. */ if (us.objCenter == 0) j = i < oMoo ? 1-i : i; else if (us.objCenter == 1) j = i; else j = i == 0 ? us.objCenter : (i == us.objCenter ? 0 : i); xp = spacex[j]; yp = spacey[j]; x[i] = cx-(int)(xp*sx); y[i] = cy+(int)(yp*sy); m[i] = x[i]; n[i] = y[i]+unit/2; } /* As in the DrawGlobe() routine, we now determine where to draw the */ /* glyphs in relation to the actual points, so that the glyphs aren't */ /* drawn on top of each other if possible. Again, we assume that we'll */ /* put the glyph right under the point, unless there would be some */ /* overlap and the above position is better off. */ for (i = 0; i <= oNorm; i++) if (FProper(i)) { k = l = gs.xWin+gs.yWin; for (j = 0; j < i; j++) if (FProper(j)) { k = Min(k, abs(m[i]-m[j])+abs(n[i]-n[j])); l = Min(l, abs(m[i]-m[j])+abs(n[i]-unit-n[j])); } if (k < unit || l < unit) if (k < l) n[i] -= unit; } /* Draw the 12 sign boundaries from the center body to edges of screen. */ a = Mod(DFromR(Angle(spacex[oJup], spacey[oJup]))-planet[oJup]); DrawColor(gi.kiGray); for (i = 0; i < cSign; i++) { k = cx+2*(int)((real)cx*RCosD((real)i*30.0+a)); l = cy+2*(int)((real)cy*RSinD((real)i*30.0+a)); DrawClip(cx, cy, k, l, x1, y1, x2, y2, 1); } DrawColor(gi.kiLite); DrawEdge(x1, y1, x2, y2); for (i = oNorm; i >= 0; i--) if (FProper(i) && FInRect(m[i], n[i], x1, y1, x2, y2)) DrawObject(i, m[i], n[i]); for (i = oNorm; i >= 0; i--) if (FProper(i) && FInRect(x[i], y[i], x1, y1, x2, y2)) { DrawColor(kObjB[i]); if (!gs.fAlt || i > oNorm) DrawPoint(x[i], y[i]); /* Draw small or large dot */ else /* near glyph indicating */ DrawSpot(x[i], y[i]); /* exact orbital location. */ } } /* Draw an arrow from one point to another, a line with an arrowhead at the */ /* ending point. The size of the arrowhead is based on current scale size, */ /* and the line segment is actually shorter and doesn't touch either */ /* endpoint by the same amount. This is used by XChartDispositor() below. */ void DrawArrow(x1, y1, x2, y2) int x1, y1, x2, y2; { real r, s, a; r = DFromR(Angle((real)(x2-x1), (real)(y2-y1))); s = (real)(gi.nScale*gi.nScaleT*8); x1 += (int)(s*RCosD(r)); y1 += (int)(s*RSinD(r)); /* Shrink line by */ x2 -= (int)(s*RCosD(r)); y2 -= (int)(s*RSinD(r)); /* the scale amount. */ s = (real)(gi.nScale*gi.nScaleT)*4.5; DrawLine(x1, y1, x2, y2); /* Main segment. */ for (a = -1.0; a <= 1.0; a += 2.0) DrawLine(x2, y2, x2 + (int)(s*RCosD(r + a*135.0)), /* The two arrow */ y2 + (int)(s*RSinD(r + a*135.0))); /* head line pieces. */ } /* Draw dispositor graphs for the 10 main planets, as done when the -j is */ /* combined with the -X switch. Four graphs are drawn, one in each screen */ /* quadrant. A dispositor graph may be based on the sign or house position, */ /* and the planets may be arranged in a hierarchy or a wheel format. */ void XChartDispositor() { int oDis[oMain+1], dLev[oMain+1], cLev[oMain+1], xo[oMain+1], yo[oMain+1]; real xCirc[oMain+1], yCirc[oMain+1]; char sz[cchSzDef]; int xLev, yLev, xSub, ySub, cx0, cy0, cx, cy, i, j, k; /* Set up screen positions of the 10 planets for the wheel graphs. */ cx0 = gs.xWin / 2; cy0 = gs.yWin / 2; for (i = 1; i <= oMain; i++) { if ((j = (180-(i-1)*360/oMain)) < 0) j += nDegMax; xCirc[i] = (real)cx0*0.4*RCosD((real)j); yCirc[i] = (real)cy0*0.4*RSinD((real)j); } /* Loop over the two basic dispositor types: sign based and house based. */ for (xSub = 0; xSub <= 1; xSub++) { cx = xSub * cx0 + cx0 / 2; /* For each planet, get its dispositor planet for current graph type. */ for (i = 1; i <= oMain; i++) { oDis[i] = rules[xSub ? inhouse[i] : SFromZ(planet[i])]; dLev[i] = 1; } /* Determine the final dispositors (including mutual reception loops). */ do { j = fFalse; for (i = 1; i <= oMain; i++) cLev[i] = fFalse; for (i = 1; i <= oMain; i++) if (dLev[i]) cLev[oDis[i]] = fTrue; for (i = 1; i <= oMain; i++) /* A planet isn't a final dispositor */ if (dLev[i] && !cLev[i]) { /* if nobody is pointing to it. */ dLev[i] = 0; j = fTrue; } } while (j); /* Determine the level of each planet, i.e. how many times you have to */ /* jump to your dispositor before reaching a final, with finals == 1. */ do { j = fFalse; for (i = 1; i <= oMain; i++) if (!dLev[i]) { if (!dLev[oDis[i]]) j = fTrue; else /* If my dispositor already has */ dLev[i] = dLev[oDis[i]] + 1; /* a level, mine is one more. */ } } while (j); /* Count the number of planets at each dispositor level. */ for (i = 1; i <= oMain; i++) cLev[i] = 0; for (i = 1; i <= oMain; i++) cLev[dLev[i]]++; /* Count the number of levels total, and max planets on any one level. */ xLev = yLev = 0; for (i = 1; i <= oMain; i++) if (cLev[i]) { yLev = i; if (cLev[i] > xLev) xLev = cLev[i]; } /* Loop over our two dispositor display formats: hierarchy and wheel. */ for (ySub = 0; ySub <= 1; ySub++) { cy = ySub * cy0 + cy0 / 2; sprintf(sz, "%s dispositor %s.", xSub ? "House" : "Sign", ySub ? "wheel" : "hierarchy"); DrawColor(gi.kiLite); DrawSz(sz, cx, ySub * cy0 + 3*gi.nScaleT, dtTop); if (ySub) { /* Draw a graph in wheel format. */ for (i = 1; i <= oMain; i++) { DrawObject(i, cx + (int)xCirc[i], cy + (int)yCirc[i]); j = oDis[i]; if (j != i) { if (dLev[i] < 2) DrawColor(gi.kiOn); else DrawColor(kObjB[i]); DrawArrow(cx + (int)xCirc[i], cy + (int)yCirc[i], cx + (int)xCirc[j], cy + (int)yCirc[j]); } if (!gs.fAlt && (j == i || dLev[i] < 2)) { DrawColor(j == i ? gi.kiOn : gi.kiGray); DrawCircle(cx + (int)xCirc[i], cy + (int)yCirc[i], 7*gi.nScale*gi.nScaleT, 7*gi.nScale*gi.nScaleT); } } } else { /* For level hierarchies, first figure out the screen coordinates */ /* for each planet, based on its level, total levels, and max width. */ for (i = 1; i <= oMain; i++) { yo[i] = cy0*(dLev[i]*2-1)/(yLev*2); k = 0; for (j = 1; j < i; j++) if (dLev[i] == dLev[j]) k = j; if (k) xo[i] = xo[k] + cx0/xLev; /* One right of last one on level. */ else xo[i] = cx - ((cx0/xLev)*(cLev[dLev[i]]-1)/2); } /* Draw graph in level hierarchy format. */ for (i = 1; i <= oMain; i++) { DrawObject(i, xo[i], yo[i]); j = oDis[i]; if (j != i) { if (dLev[i] < 2) { if (abs(xo[i] - xo[j]) < cx0/xLev*3/2) { DrawColor(gi.kiOn); DrawArrow(xo[i], yo[i], xo[j], yo[j]); } DrawColor(gi.kiGray); } else { DrawColor(kObjB[i]); DrawArrow(xo[i], yo[i], xo[j], yo[j]); } } else DrawColor(gi.kiOn); if (!gs.fAlt && dLev[i] < 2) DrawCircle(xo[i], yo[i], 7*gi.nScale*gi.nScaleT, 7*gi.nScale*gi.nScaleT); } } } } /* Draw boundary lines between the four separate dispositor graphs. */ if (gs.fBorder) { DrawColor(gi.kiLite); DrawBlock(cx0, 0, cx0, gs.yWin); DrawBlock(0, cy0, gs.xWin, cy0); } } /* Draw a graphical calendar on the screen for the chart month, with */ /* numbers in boxes, as done when the -K is combined with the -X switch. */ void XChartCalendar() { char sz[cchSzDef]; int day, cday, dayHi, cweek, xunit, yunit, x1, y1, x, y, s; day = DayOfWeek(Mon, 1, Yea); /* Day of week of 1st of month. */ cday = DaysInMonth(Mon, Yea); /* Count of days in the month. */ dayHi = DayInMonth(Mon, Yea); /* Number of last day in the month. */ cweek = us.fCalendarYear ? 6 : (day + cday + 6) / 7; /* Week rows. */ xunit = gs.xWin/8; /* Hor. pixel size of each day box. */ yunit = gs.yWin/(cweek+2); /* Ver. pixel size of each day box. */ x1 = (gs.xWin - xunit*7) / 2; /* Blank space to left of calendar. */ y1 = yunit*3/2; /* Blank space to top of calendar. */ /* Print the month and year in big letters at top of chart. */ DrawColor(gi.kiOn); sprintf(sz, "%s, %d", szMonth[Mon], Yea); s = gi.nScaleT; gi.nScaleT = Min((yunit*3/2-yFontT*gi.nScale) / yFont, gs.xWin/CchSz(sz) / xFont); gi.nScaleT = Max(gi.nScaleT-1, 1); DrawSz(sz, gs.xWin/2, (yunit*3/2-yFont*gi.nScale)/2, dtCent); /* Draw the grid of boxes for the days. */ gi.nScaleT = gi.nScale; for (x = 0; x <= 7; x++) { /* Print days of week at top of each column (abbreviated if need be). */ if (xunit / xFontT < 9) sprintf(sz, "%c%c%c", chDay3(x)); else sprintf(sz, "%s", szDay[x]); if (x < 7) { DrawColor(kRainbowB[3]); DrawSz(sz, x1 + x*xunit + xunit/2, y1 - s*3, dtBottom); DrawColor(kRainbowB[5]); } DrawLine(x1 + x*xunit, y1, x1 + x*xunit, y1 + cweek*yunit); } for (y = 0; y <= cweek; y++) DrawLine(x1, y1 + y*yunit, x1 + 7*xunit, y1 + y*yunit); /* Actually draw the day numbers in their appropriate boxes. */ x = day; y = 0; for (day = 1; day <= dayHi; day = AddDay(Mon, day, Yea, 1)) { sprintf(sz, gs.fText ? "%2d" : "%d", day); DrawColor(day == Day && gs.fLabel ? kRainbowB[4] : (x <= 0 || x >= 6 ? kRainbowB[1] : gi.kiLite)); if (!gs.fAlt) DrawSz(sz, x1 + x*xunit + s*2, y1 + y*yunit + s*4, dtLeft | dtTop); else DrawSz(sz, x1 + x*xunit + xunit/2, y1 + y*yunit + yunit/2 + gi.nScale, dtCent); if (++x >= 7) { x = 0; y++; } } gi.nScaleT = s; } /* Draw a chart showing a graphical ephemeris for the given month (or year */ /* if -Ey in effect), with the date on the vertical access and the zodiac */ /* on the horizontal, as done when the -E is combined with the -X switch. */ void XChartEphemeris() { real symbol[cObj*2+1], objSav[objMax]; char sz[4]; int yea, unit = 6*gi.nScale, daytot, d = 1, day, mon, monsiz, x1, y1, x2, y2, xs, ys, m, n, u, v, i, j; yea = us.nEphemYears; /* Is this -Ey -X or just -E -X? */ if (yea) { daytot = DayInYearHi(Yea); day = 1; mon = 1; monsiz = 31; } else daytot = DayInMonth(Mon, Yea); x1 = yea ? 30 : 24; y1 = unit*2; x2 = gs.xWin - x1; y2 = gs.yWin - y1; xs = x2 - x1; ys = y2 - y1; /* Display glyphs of the zodiac along the bottom axis. */ for (i = 1; i <= cSign+1; i++) { m = x1 + xs * (i-1) / 12; j = i > cSign ? 1 : i; DrawColor(kSignB(j)); DrawSign(j, m, y2 + unit); DrawColor(gi.kiGray); DrawDash(m, y1, m, y2, 2); } /* Loop and display planet movements for one day segment. */ while (d <= daytot + 1) { n = v; v = y1 + NMultDiv(ys, d-1, daytot); if (!yea || day == 1) { DrawColor(gi.kiGray); DrawDash(x1, v, x2, v, 1); /* Marker line for day or month. */ } if (d > 1) for (i = 1; i <= cObj; i++) objSav[i] = planet[i]; if (yea) { MM = mon; DD = day; } else { MM = Mon; DD = d; } YY = Yea; TT = 0.0; SS = us.dstDef; ZZ = us.zonDef; OO = us.lonDef; AA = us.latDef; CastChart(fTrue); /* Draw planet glyphs along top of chart. */ if (d < 2) { for (i = 1; i <= cObj; i++) { symbol[i*2-1] = -rLarge; if (!FProper(i) || (i == oMoo && gs.fAlt)) symbol[i*2] = -rLarge; else symbol[i*2] = planet[i]; } FillSymbolLine(symbol); for (i = cObj; i >= 1; i--) if (symbol[i*2] >= 0.0) DrawObject(i, x1 + (int)((real)xs * symbol[i*2] / rDegMax), unit); /* Draw a line segment for each object during this time section. */ } else for (i = cObj; i >= 1; i--) { if (!FProper(i) || (i == oMoo && gs.fAlt)) continue; m = x1 + (int)((real)xs * objSav[i] / rDegMax); u = x1 + (int)((real)xs * planet[i] / rDegMax); DrawColor(kObjB[i]); DrawWrap(m, n, u, v, x1, x2); } /* Label months or days in the month along the left and right edges. */ if (d <= daytot && (!yea || day == 1)) { if (yea) { sprintf(sz, "%c%c%c", chMon3(mon)); i = (mon == Mon); } else { sprintf(sz, "%2d", d); i = (d == Day); } DrawColor(i ? gi.kiOn : gi.kiLite); DrawSz(sz, xFont *gi.nScaleT, v + (yFont-2)*gi.nScaleT, dtLeft | dtBottom); DrawSz(sz, x2+(xFont-1)*gi.nScaleT, v + (yFont-2)*gi.nScaleT, dtLeft | dtBottom); } /* Now increment the day counter. For a month we always go up by one. */ /* For a year we go up by four or until the end of the month reached. */ if (yea) { day += 4; if (day > monsiz) { d += 4-(day-monsiz-1); if (d <= daytot + 1) { mon++; monsiz = DayInMonth(mon, Yea); day = 1; } } else d += 4; } else d++; } DrawColor(gi.kiLite); DrawEdge(x1, y1, x2, y2); ciCore = ciMain; /* Recast original chart. */ CastChart(fTrue); } #endif /* GRAPH */ /* xcharts1.c */