This is xgeneral.c in view mode; [Download] [Up]
/* ** Astrolog (Version 4.10) File: xgeneral.c ** ** IMPORTANT NOTICE: the graphics database and chart display routines ** used in this program are Copyright (C) 1991-1994 by Walter D. Pullen ** (cruiser1@stein.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 3/19/1994. */ #include "astrolog.h" #ifdef GRAPH int xpen, ypen; /* ****************************************************************************** ** Bitmap File Routines. ****************************************************************************** */ /* Write the bitmap array to a previously opened file in a format that */ /* can be read in by the Unix X commands bitmap and xsetroot. The 'mode' */ /* parameter defines how much white space is put in the file. */ void WriteXBitmap(data, name, mode) FILE *data; char *name, mode; { int x, y, i, temp = 0; _int value; fprintf(data, "#define %s_width %d\n" , name, chartx); fprintf(data, "#define %s_height %d\n", name, charty); fprintf(data, "static %s %s_bits[] = {", mode != 'V' ? "char" : "short", name); for (y = 0; y < charty; y++) { x = 0; do { /* Process each row, eight columns at a time. */ if (y + x > 0) fprintf(data, ","); if (temp == 0) fprintf(data, "\n%s", mode == 'N' ? " " : (mode == 'C' ? " " : "")); value = 0; for (i = (mode != 'V' ? 7 : 15); i >= 0; i--) value = (value << 1) + (!(PGET(bm, x+i, y)^(xreverse*15))^xreverse && (x + i < chartx)); if (mode == 'N') putc(' ', data); fprintf(data, "0x"); if (mode == 'V') fprintf(data, "%c%c", INTTOHEX(value >> 12), INTTOHEX((value >> 8) & 15)); fprintf(data, "%c%c", INTTOHEX((value >> 4) & 15), INTTOHEX(value & 15)); temp++; /* Is it time to skip to the next line while writing the file yet? */ if ((mode == 'N' && temp >= 12) || (mode == 'C' && temp >= 15) || (mode == 'V' && temp >= 11)) temp = 0; x += (mode != 'V' ? 8 : 16); } while (x < chartx); } fprintf(data, "};\n"); } /* Write the bitmap array to a previously opened file in a simple boolean */ /* Ascii rectangle, one char per pixel, where '#' represents an off bit and */ /* '-' an on bit. The output format is identical to the format generated by */ /* the Unix bmtoa command, and it can be converted into a bitmap with atobm. */ void WriteAscii(data) FILE *data; { int x, y, i; for (y = 0; y < charty; y++) { for (x = 0; x < chartx; x++) { i = PGET(bm, x, y); if (xcolor) putc(INTTOHEX(i), data); else putc(i ? '-' : '#', data); } putc('\n', data); } } /* Write the bitmap array to a previously opened file in the bitmap format */ /* used in Microsoft Windows for its .bmp extension files. This is a pretty */ /* efficient format, only requiring one bit per pixel and a small header. */ void WriteBmp(data) FILE *data; { int x, y; dword value; /* BitmapFileHeader */ PutByte('B'); PutByte('M'); PutLong(14+40 + (xcolor ? 64 : 8) + (long)4*charty*((chartx-1 >> (xcolor ? 3 : 5))+1)); PutWord(0); PutWord(0); PutLong(14+40 + (xcolor ? 64 : 8)); /* BitmapInfo / BitmapInfoHeader */ PutLong(40); PutLong(chartx); PutLong(charty); PutWord(1); PutWord(xcolor ? 4 : 1); PutLong(0 /*BI_RGB*/); PutLong(0); PutLong(0); PutLong(0); PutLong(0); PutLong(0); /* RgbQuad */ if (xcolor) for (x = 0; x < 16; x++) { PutByte(RGBB(rgbbmp[x])); PutByte(RGBG(rgbbmp[x])); PutByte(RGBR(rgbbmp[x])); PutByte(0); } else { PutLong(0); PutByte(255); PutByte(255); PutByte(255); PutByte(0); } /* Data */ for (y = charty-1; y >= 0; y--) { value = 0; for (x = 0; x < chartx; x++) { if ((x & (xcolor ? 7 : 31)) == 0 && x > 0) { PutLong(value); value = 0; } if (xcolor) value |= (dword)PGET(bm, x, y) << ((x & 7 ^ 1) << 2); else if (PGET(bm, x, y)) value |= (dword)1 << (x & 31 ^ 7); } PutLong(value); } } /* Output the bitmap in memory to a file. This basically consists of just */ /* calling some routine to actually write a bitmap to a file, although we */ /* need to prompt the user for a filename if it wasn't specified beforehand. */ void WriteFile() { char line[STRING]; FILE *data; #ifdef PS if (psfile) { PSend(); return; } #endif if (outputfile == NULL && (metafile || (xbitmap && bitmapmode == 'B'))) fprintf(stdout, "(It is recommended to specify an extension of '.%s'.)\n", xbitmap ? "bmp" : "wmf"); loop { if (outputfile == NULL) { sprintf(line, "Enter name of file to write %s to", xbitmap ? "bitmap" : "metafile"); InputString(line, line); outputfile = line; } data = fopen(outputfile, "wb"); if (data != NULL) break; else { PrintWarning("Couldn't create output file."); outputfile = NULL; } } if (xbitmap) { if (bitmapmode == 'B') WriteBmp(data); else if (bitmapmode == 'A') WriteAscii(data); else WriteXBitmap(data, outputfile, bitmapmode); } #ifdef META else WriteMeta(data); #endif fclose(data); } /* ****************************************************************************** ** PostScript File Routines. ****************************************************************************** */ #ifdef PS /* Global variables used by the PostScript generator. */ FILE *psdata; int strokecount = 0, currentlinecap = 0, currentdash = 0, currentfont = 0; real currentlinewidth = 1.0; /* Table of PostScript header alias lines used by the program. */ char PSfunctions[] = "/languagelevel where{pop languagelevel}{1}ifelse\ 2 lt{\n\ /sf{exch findfont exch\ dup type/arraytype eq{makefont}{scalefont}ifelse setfont}bind def\n\ /rf{gsave newpath\n\ 4 -2 roll moveto\ dup 0 exch rlineto exch 0 rlineto neg 0 exch rlineto closepath\n\ fill grestore}bind def\n\ /rc{newpath\n\ 4 -2 roll moveto\ dup 0 exch rlineto exch 0 rlineto neg 0 exch rlineto closepath\n\ clip newpath}bind def\n\ }{/sf/selectfont load def/rf/rectfill load def\ /rc/rectclip load def}ifelse\n\ /center{0 begin gsave dup 4 2 roll\ translate newpath 0 0 moveto\ false charpath flattenpath pathbbox\ /URy exch def/URx exch def/LLy exch def/LLx exch def\ URx LLx sub 0.5 mul LLx add neg URy LLy sub 0.5 mul LLy add neg\ 0 0 moveto rmoveto\ show grestore end}bind def\n\ /center load 0 4 dict put\n\ /c{setrgbcolor}bind def\n\ /d{moveto 0 0 rlineto}bind def\n\ /l{4 2 roll moveto lineto}bind def\n\ /t{lineto}bind def\n\ /el{newpath matrix currentmatrix 5 1 roll translate scale\ 0 0 1 0 360 arc setmatrix stroke}bind def\n"; /* Write a command to flush the PostScript buffer. */ void PSforcestroke() { if (strokecount > 0) { /* render any existing path */ fprintf(psdata, "stroke\n"); strokecount = 0; xpen = -1; /* Invalidate PolyLine cache */ } } /* Indicate that a certain number of PostScript commands have been done. */ void PSstroke(n) int n; { strokecount += n; if (strokecount > 5000) /* Whenever we reach a certain limit, flush. */ PSforcestroke(); } /* Set the type of line end to be used by PostScript commands. If linecap */ /* is true, then the line ends are rounded, otherwise they are squared. */ void PSlinecap(linecap) int linecap; { if (linecap != currentlinecap) { PSforcestroke(); fprintf(psdata, "%d setlinecap\n", linecap); currentlinecap = linecap; } } /* Set the dash length to be used by PostScript line commands. */ void PSdash(dashoff) int dashoff; { if (dashoff != currentdash) { PSforcestroke(); if (dashoff) fprintf(psdata, "[%d %d", PSMUL, dashoff * PSMUL); else fprintf(psdata, "["); fprintf(psdata, "]0 setdash\n"); currentdash = dashoff; } } /* Set a linewidth size to be used by PostScript figure primitive commands. */ void PSlinewidth(linewidth) int linewidth; { if (linewidth != currentlinewidth) { PSforcestroke(); fprintf(psdata, "%d setlinewidth\n", linewidth); currentlinewidth = linewidth; } } /* Set a system font and size to be used by PostScript text commands. */ void PSfont(psfont) int psfont; { int temp; if (psfont != currentfont && xfont) { if (psfont <= 2) { temp = psfont == 1 ? 32*PSMUL : 23*PSMUL; fprintf(psdata, "/Astro[%d 0 0 -%d 0 0]sf\n", temp, temp); } else if (psfont == 3) { temp = 26*PSMUL; fprintf(psdata, "/Times-Roman[%d 0 0 -%d 0 0]sf\n", temp, temp); } else { temp = 10*PSMUL; fprintf(psdata, "/Courier[%d 0 0 -%d 0 0]sf\n", temp, temp); } currentfont = psfont; } } /* Prompt the user for the name of a file to write the PostScript file to */ /* (if not already specified), open it, and write out file header info. */ void PSbegin() { char line[STRING]; if (outputfile == NULL && epsfile) fprintf(stdout, "(It is recommended to specify an extension of '.eps'.)\n"); loop { if (outputfile == NULL) { sprintf(line, "Enter name of file to write PostScript to"); InputString(line, line); outputfile = line; } psdata = fopen(outputfile, "w"); if (psdata != NULL) break; else { PrintWarning("Couldn't create output file."); outputfile = NULL; } } fprintf(psdata, "%%!PS-Adobe-2.0"); if (epsfile) fprintf(psdata, " EPSF-2.0"); fprintf(psdata, "\n%%%%Title: %s\n", outputfile); fprintf(psdata, "%%%%Creator: %s %s\n", appname, VERSION); fprintf(psdata, "%%%%CreationDate: %s\n", DATE); if (epsfile) { fprintf(psdata, "%%%%BoundingBox: 0 0 %d %d\n", chartx, charty); fprintf(psdata, "%%%%EndComments\n"); fprintf(psdata, "%%%%BeginSetup\n"); fprintf(psdata, PSfunctions, 6 * PSMUL, 6 * PSMUL); fprintf(psdata, "%%%%EndSetup\n"); fprintf(psdata, "0 0 %d %d rc\n", chartx, charty); } else { fprintf(psdata, "%%%%Pages: 1 1\n"); fprintf(psdata, "%%%%DocumentFonts: (atend)\n"); fprintf(psdata, "%%%%BoundingBox: 9 9 %d %d\n", (int)(psinchx*72.0+ROUND)-9, (int)(psinchy*72.0+ROUND)-9); fprintf(psdata, "%%%%EndComments\n"); fprintf(psdata, "%%%%BeginProcSet: common\n"); fprintf(psdata, PSfunctions, 6 * PSMUL, 6 * PSMUL); fprintf(psdata, "%%%%EndProcSet\n"); fprintf(psdata, "%%%%Page: 1 1\n"); } PSfont(2); fprintf(psdata, "gsave\n"); PSlinewidth(metawid/2); xpen = -1; } /* Write out trailing information to the PostScript file and close it. */ void PSend() { PSforcestroke(); if (epsfile) fprintf(psdata, "%%%%EOF\n"); else { fprintf(psdata, "showpage\n"); fprintf(psdata, "%%%%PageTrailer\n"); fprintf(psdata, "%%%%Trailer\n"); fprintf(psdata, "%%%%DocumentFonts: Times-Roman\n"); if (xfont) { fprintf(psdata, "%%%%+ Courier\n"); fprintf(psdata, "%%%%+ Astro\n"); } } fclose(psdata); } #endif /* PS */ /* ****************************************************************************** ** Metafile Routines. ****************************************************************************** */ #ifdef META /* Global variables used by the metafile generator. */ colpal metalinedes, metalineact = -1, /* Desired and actual line color. */ metafilldes, metafillact = -1, /* Desired and actual fill color. */ metafontdes = -1, metafontact = -1, /* Desired and actual text font. */ metatxtcdes = -1, metatxtcact = -1, /* Desired and actual text color. */ metatxtades = -1, metatxtaact = -1; /* Desired/actual text alignment. */ /* Macros to output the various metafile commands we use. */ #define MetaRecord(S, R) MetaLong((long)(S)); MetaWord(R) #define MetaSelectObject(O) MetaRecord(4, 0x12D); MetaWord(O) #define MetaDeleteObject(O) MetaRecord(4, 0x1F0); MetaWord(O) #define MetaSaveDc() MetaRecord(3, 0x01E) #define MetaRestoreDc() MetaRecord(4, 0x127); MetaWord(-1) #define MetaWindowOrg(X, Y) MetaRecord(5, 0x20B); MetaWord(Y); MetaWord(X) #define MetaWindowExt(X, Y) MetaRecord(5, 0x20C); MetaWord(Y); MetaWord(X) #define MetaCreatePen(S, W, C) MetaRecord(8, 0x2FA); MetaWord(S); \ MetaWord(W); MetaWord(W); MetaLong(C) #define MetaCreateBrush(S, C) MetaRecord(7, 0x2FC); \ MetaWord(S); MetaLong(C); MetaWord(0 /* Not used */); #define MetaCreateFont(S, X, Y, C) MetaRecord(12+(S), 0x2FB); MetaWord(Y); \ MetaWord(X); MetaWord(0 /* Angle */); MetaWord(0 /* Not used */); \ MetaWord(400 /* Normal Weight */); MetaWord(0 /* Italic, Underline */); \ MetaWord(MAKEWORD(0 /* Strikeout */, C)); \ MetaWord(MAKEWORD(4 /* TrueType */, 0 /* Clip */)) #define MetaBkMode(M) MetaRecord(4, 0x102); MetaWord(M) #define MetaTextAlign(A) MetaRecord(4, 0x12E); MetaWord(A) #define MetaTextColor(C) MetaRecord(5, 0x209); MetaLong(C); #define MetaTextOut(X, Y, S) MetaRecord(7+((S)+1)/2, 0xA32); \ MetaWord(Y); MetaWord(X); MetaWord(S); MetaWord(0 /* ETO */) #define MetaRectangle(X1, Y1, X2, Y2) MetaRecord(7, 0x41B); \ MetaWord(Y2); MetaWord(X2); MetaWord(Y1); MetaWord(X1) #define MetaEllipse(X1, Y1, X2, Y2) MetaRecord(7, 0x418); \ MetaWord(Y2); MetaWord(X2); MetaWord(Y1); MetaWord(X1) #define MetaEscape(S) MetaRecord((S), 0x626); \ MetaWord(15 /* MFCOMMENT */); MetaWord(((S)-5)*2 /* Bytes in comment */); /* Output one 16 bit or 32 bit value into the metafile buffer stream. */ void MetaWord(w) word w; { char string[STRING]; if ((byte PTR)metacur - bm >= MAXMETA) { sprintf(string, "Metafile would be more than %ld bytes.", MAXMETA); PrintError(string); Terminate(_FATAL); } *metacur = w; metacur++; } void MetaLong(l) long l; { MetaWord(LOWORD(l)); MetaWord(HIWORD(l)); } /* Output any necessary metafile records to make the current actual */ /* settings of line color, fill color, etc, be those that we know are */ /* desired. This is generally called by the primitives routines before */ /* any figure record is actually written into a metafile. We wait until */ /* the last moment before changing any settings to ensure that we don't */ /* output any unnecessary records, e.g. two select colors in a row. */ void MetaSelect() { if (metalinedes != metalineact) { MetaSelectObject(metalinedes); metalineact = metalinedes; } if (metafilldes != metafillact) { MetaSelectObject(16*4 + metafilldes); metafillact = metafilldes; } if (metafontdes != metafontact) { MetaSelectObject(16*5 + metafontdes); metafontact = metafontdes; } if (metatxtcdes != metatxtcact) { MetaTextColor(rgbbmp[metatxtcdes]); metatxtcact = metatxtcdes; } if (metatxtades != metatxtaact) { MetaTextAlign(metatxtades); metatxtaact = metatxtades; } xpen = -1; /* Invalidate PolyLine cache */ } /* Output initial metafile header information into our metafile buffer. */ /* We also setup and create all pen, brush, and font objects that may */ /* possibly be used in the generation and playing of the picture. */ void MetaInit() { int i, j, k; metacur = (word PTR)bm; /* Placable Metaheader */ MetaLong(0x9AC6CDD7L); MetaWord(0); /* Not used */ MetaWord(0); MetaWord(0); MetaWord(chartx); MetaWord(charty); MetaWord(chartx/6); /* Units per inch */ MetaLong(0L); /* Not used */ MetaWord(0x9AC6 ^ 0xCDD7 ^ chartx ^ charty ^ chartx/6); /* Checksum */ /* Metaheader */ MetaWord(1); /* Metafile type */ MetaWord(9); /* Size of header in words */ MetaWord(0x300); /* Windows version */ MetaLong(0L); /* Size of entire metafile in words */ MetaWord(16*5+1+(xfont>0)*4); /* Number of objects in metafile */ MetaLong(17L); /* Size of largest record in words */ MetaWord(0); /* Not used */ /* Setup */ MetaEscape(17); MetaLong(MAKEQUAD('A', 's', 't', 'r')); /* "Astr" */ MetaWord(4); /* Creator */ MetaLong(14L); /* Bytes in string */ MetaLong(MAKEQUAD('A', 's', 't', 'r')); /* "Astr" */ MetaLong(MAKEQUAD('o', 'l', 'o', 'g')); /* "olog" */ MetaLong(MAKEQUAD(' ', '4', '.', '1')); /* " 4.1" */ MetaWord(MAKEWORD('0', 0)); /* "0" */ MetaSaveDc(); MetaWindowOrg(0, 0); MetaWindowExt(chartx, charty); MetaBkMode(1 /* Transparent */); /* Colors */ for (j = 1; j <= 4; j++) for (i = 0; i < 16; i++) { k = j <= 1 ? metawid : 0; MetaCreatePen(j <= 2 ? 0 : j-2 /* PS_SOLID; PS_DASH; PS_DOT */, k, rgbbmp[i]); } for (i = 0; i < 16; i++) { MetaCreateBrush(0 /* BS_SOLID */, rgbbmp[i]); } MetaCreateBrush(1 /* BS_NULL */, 0L); /* Fonts */ if (xfont) { MetaCreateFont(5, 0, -8*SCALE, 2 /* Symbol Charset */); MetaWord(MAKEWORD(1 /* Draft */, 1 | 0x10 /* Fixed | Roman */)); MetaLong(MAKEQUAD('W', 'i', 'n', 'g')); MetaLong(MAKEQUAD('d', 'i', 'n', 'g')); MetaWord(MAKEWORD('s', 0)); MetaCreateFont(8, 0, -6*SCALE, 0 /* Ansi Charset */); MetaWord(MAKEWORD(0 /* Default */, 2 | 0x10 /* Variable | Roman */)); MetaLong(MAKEQUAD('T', 'i', 'm', 'e')); MetaLong(MAKEQUAD('s', ' ', 'N', 'e')); MetaLong(MAKEQUAD('w', ' ', 'R', 'o')); MetaLong(MAKEQUAD('m', 'a', 'n', 0)); MetaCreateFont(6, 6*METAMUL, 10*METAMUL, 0 /* Ansi Charset */); MetaWord(MAKEWORD(1 /* Draft */, 1 | 0x30 /* Fixed | Modern */)); MetaLong(MAKEQUAD('C', 'o', 'u', 'r')); MetaLong(MAKEQUAD('i', 'e', 'r', ' ')); MetaLong(MAKEQUAD('N', 'e', 'w', 0)); MetaCreateFont(8, 0, -11*SCALE, 0 /* Ansi Charset */); MetaWord(MAKEWORD(0 /* Default */, 2 | 0 /* Variable | Don't Care */)); MetaLong(MAKEQUAD('A', 's', 't', 'r')); MetaLong(MAKEQUAD('o', '-', 'S', 'e')); MetaLong(MAKEQUAD('m', 'i', 'B', 'o')); MetaLong(MAKEQUAD('l', 'd', 0, 0)); } } /* Output trailing records to indicate the end of the metafile and then */ /* actually write out the entire buffer to the specifed file. */ void WriteMeta(data) FILE *data; { word PTR w; #if FALSE int i; for (i = 16*5+1+(xfont>0)*4; i >= 0; i--) { MetaDeleteObject(i); } #endif MetaRestoreDc(); MetaRecord(3, NULL); /* End record */ *(long PTR)(bm + 22 + 6) = ((long)((byte PTR)metacur - bm) - 22) / 2; for (w = (word PTR)bm; w < metacur; w++) { PutWord(*w); } } #endif /* META */ /* ****************************************************************************** ** Core Graphic Procedures. ****************************************************************************** */ /* Set the current color to use in drawing on the screen or bitmap array. */ void DrawColor(col) colpal col; { if (xfile) { #ifdef PS if (psfile) { if (colcur != col) { PSforcestroke(); /* Render existing path with current color */ fprintf(psdata, "%.2f %.2f %.2f c\n", (real)RGBR(rgbbmp[col])/255.0, (real)RGBG(rgbbmp[col])/255.0, (real)RGBB(rgbbmp[col])/255.0); } } #endif #ifdef META if (metafile) metalinedes = col; #endif } #ifdef X11 else XSetForeground(disp, gc, rgbind[col]); #endif #ifdef MSG else _setcolor(col); #endif colcur = col; } /* Set a single point on the screen. This is the most basic graphic function */ /* and is called by all the more complex routines. Based on what mode we are */ /* in, we either set a cell in the bitmap array or a pixel on the window. */ void DrawPoint(x, y) int x, y; { if (xfile) { if (xbitmap) { /* Force the coordinates to be within the bounds of the bitmap array. */ if (x < 0) x = 0; else if (x >= chartx) x = chartx-1; if (y < 0) y = 0; else if (y >= charty) y = charty-1; PSET(bm, x, y, colcur); } #ifdef PS else if (psfile) { DrawColor(colcur); PSlinecap(TRUE); fprintf(psdata, "%d %d d\n", x, y); PSstroke(2); } #endif #ifdef META else { metafilldes = colcur; MetaSelect(); MetaEllipse(x-metawid/2, y-metawid/2, x+metawid/2, y+metawid/2); } #endif } #ifdef X11 else XDrawPoint(disp, pixmap, gc, x, y); #endif #ifdef MSG else _setpixel(offsetx + x, offsety + y); #endif } /* Draw dot a little larger than just a single pixel at specified location. */ void DrawSpot(x, y) int x, y; { #ifdef PS if (psfile) { PSlinewidth(currentlinewidth*3); DrawPoint(x, y); PSlinewidth(currentlinewidth/3); return; } #endif #ifdef META if (metafile) { metafilldes = colcur; MetaSelect(); MetaEllipse(x-metawid, y-metawid, x+metawid, y+metawid); return; } #endif DrawPoint(x, y); DrawPoint(x, y-1); DrawPoint(x-1, y); DrawPoint(x+1, y); DrawPoint(x, y+1); } /* Draw a filled in block, defined by the corners of its rectangle. */ void DrawBlock(x1, y1, x2, y2) int x1, y1, x2, y2; { int x, y; if (xfile) { if (xbitmap) { for (y = y1; y <= y2; y++) /* For bitmap, we have to */ for (x = x1; x <= x2; x++) /* just fill in the array. */ PSET(bm, x, y, colcur); } #ifdef PS else if (psfile) { DrawColor(colcur); fprintf(psdata, "%d %d %d %d rf\n", MAX(x1-metawid/4, 0), MAX(y1-metawid/4, 0), x2-x1+metawid/4, y2-y1+metawid/4); } #endif #ifdef META else { metafilldes = colcur; MetaSelect(); MetaRectangle(x1-metawid/2, y1-metawid/2, x2+metawid/2, y2+metawid/2); } #endif } #ifdef X11 else XFillRectangle(disp, pixmap, gc, x1, y1, x2-x1, y2-y1); #endif #ifdef MSG else _rectangle(_GFILLINTERIOR, offsetx + x1, offsety + y1, offsetx + x2, offsety + y2); #endif } /* Draw a rectangle on the screen with specified thickness. This is just */ /* like DrawBlock() except that we are only drawing the edges of the area. */ void DrawBox(x1, y1, x2, y2, xsiz, ysiz) int x1, y1, x2, y2, xsiz, ysiz; { #ifdef META if (metafile) /* For thin boxes in metafiles, we can just output one rectangle record */ /* instead of drawing each side separately as we have to do otherwise. */ if (xsiz <= 1 && ysiz <= 1) { metafilldes = 16; /* Specify a hollow fill brush. */ MetaSelect(); MetaRectangle(x1, y1, x2, y2); return; } #endif DrawBlock(x1, y1, x2, y1 + ysiz - 1); DrawBlock(x1, y1 + ysiz, x1 + xsiz - 1, y2 - ysiz); DrawBlock(x2 - xsiz + 1, y1 + ysiz, x2, y2 - ysiz); DrawBlock(x1, y2 - ysiz + 1, x2, y2); } /* Clear and erase the graphics screen or bitmap contents. */ void DrawClearScreen() { #ifdef PS if (psfile) { /* For PostScript charts first output page orientation information. */ if (!epsfile) { if (psinchz == 0) psinchz = chartx > charty ? -1 : 1; if (psinchz < 0) { /* Chartx and charty are reversed for Landscape mode. */ fprintf(psdata, "%d %d translate\n", ((int)(psinchx*72.0+ROUND) + charty)/2, ((int)(psinchy*72.0+ROUND) + chartx)/2); fprintf(psdata, "-90 rotate\n"); } else { /* Most charts are in Portrait mode */ fprintf(psdata, "%d %d translate\n", ((int)(psinchx*72.0+ROUND) - chartx)/2, ((int)(psinchy*72.0+ROUND) + charty)/2); } } else fprintf(psdata, "0 %d translate\n", charty); fprintf(psdata, "1 -1 scale\n"); scale *= PSMUL; chartx *= PSMUL; charty *= PSMUL; fprintf(psdata, "1 %d div dup scale\n", PSMUL); } #endif #ifdef META if (metafile) MetaInit(); /* For metafiles first go write our header information. */ #endif /* Hack: If a comparison relationship chart is set and we're in the -Z */ /* horizon or -S space graphics chart modes (which normally is just the */ /* same as single chart graphics) don't actually clear the screen. */ if (relation <= DASHr0 && xnow > 0 && (modex == MODEZ || modex == MODES)) return; #ifdef MSG if (!xfile) _clearscreen(_GCLEARSCREEN); #endif DrawColor(off); DrawBlock(0, 0, chartx - 1, charty - 1); /* Clear bitmap screen. */ } /* Draw a line on the screen, specified by its endpoints. In addition, we */ /* have specified a skip factor, which allows us to draw dashed lines. */ void DrawDash(x1, y1, x2, y2, skip) int x1, y1, x2, y2, skip; { #ifdef META static word PTR poly; #endif int x = x1, y = y1, xadd, yadd, yinc, xabs, yabs, i, j = 0; if (skip < 0) skip = 0; #ifdef ISG if (!xfile) { if (!skip) { #ifdef X11 /* For non-dashed X window lines, let's have the Xlib do it for us. */ XDrawLine(disp, pixmap, gc, x1, y1, x2, y2); #else /* For non-dashed lines, let's have the graphics library do it for us. */ _moveto(offsetx + x1, offsety + y1); _lineto(offsetx + x2, offsety + y2); #endif return; } } #endif /* ISG */ #ifdef PS if (psfile) { /* For PostScript charts we can save file size if we output a LineTo */ /* command when the start vertex is the same as the end vertex of the */ /* previous line drawn, instead of writing out both vertices. */ PSlinecap(TRUE); PSdash(skip); if (xpen != x1 || ypen != y1) fprintf(psdata, "%d %d %d %d l\n", x1, y1, x2, y2); else fprintf(psdata, "%d %d t\n", x2, y2); xpen = x2; ypen = y2; PSstroke(2); return; } #endif #ifdef META if (metafile) { /* For metafile charts we can really save file size for consecutive */ /* lines sharing endpoints by consolidating them into a PolyLine. */ if (xpen != x1 || ypen != y1) { metalinedes = (metalinedes & 15) + 16*(skip > 3 ? 3 : skip); MetaSelect(); poly = metacur; MetaRecord(8, 0x325); /* Polyline */ MetaWord(2); MetaWord(x1); MetaWord(y1); } else { *poly += 2; (*(poly+3))++; /* Note: We should technically update the max record size in the */ /* file header if need be here too, but it doesn't seem necessary. */ } MetaWord(x2); MetaWord(y2); xpen = x2; ypen = y2; return; } #endif /* If none of the above cases hold, we have to draw the line dot by dot. */ xadd = x2 - x1 >= 0 ? 1 : 3; yadd = y2 - y1 >= 0 ? 2 : 4; xabs = abs(x2 - x1); yabs = abs(y2 - y1); /* Technically what we're doing here is drawing a line which is more */ /* horizontal then vertical. We always increment x by 1, and increment */ /* y whenever a fractional variable passes a certain amount. For lines */ /* that are more vertical than horizontal, we just swap x and y coords. */ if (xabs < yabs) { SWAP(xadd, yadd); SWAP(xabs, yabs); } yinc = (xabs >> 1) - ((xabs & 1 ^ 1) && xadd > 2); for (i = xabs+1; i; i--) { if (j < 1) DrawPoint(x, y); j = j < skip ? j+1 : 0; switch (xadd) { case 1: x++; break; case 2: y++; break; case 3: x--; break; case 4: y--; break; } yinc += yabs; if (yinc - xabs >= 0) { yinc -= xabs; switch (yadd) { case 1: x++; break; case 2: y++; break; case 3: x--; break; case 4: y--; break; } } } } /* Draw a normal line on the screen; however, if the x coordinates are close */ /* to either of the two given bounds, then we assume that the line runs off */ /* one side and reappears on the other, so draw the appropriate two lines */ /* instead. This is used by the Ley line and astro-graph routines, which */ /* draw lines running around the world and hence off the edges of the maps. */ void DrawWrap(x1, y1, x2, y2, xmin, xmax) int x1, y1, x2, y2; { int xmid, ymid, i; if (x1 < 0) { /* Special case for drawing world map. */ DrawPoint(x2, y2); return; } xmid = (xmax-xmin) / 2; /* If endpoints aren't near opposite edges, just draw the line and return. */ if (abs(x2-x1) < xmid) { DrawLine(x1, y1, x2, y2); return; } i = (xmax-xmin+1) + (x1 < xmid ? x1-x2 : x2-x1); /* Determine vertical coordinate where our line runs off edges of screen. */ ymid = y1+(int)((real)(y2-y1)* (x1 < xmid ? (real)(x1-xmin) : (real)(xmax-x1))/(real)i + ROUND); DrawLine(x1, y1, x1 < xmid ? xmin : xmax, ymid); DrawLine(x2 < xmid ? xmin : xmax, ymid, x2, y2); } /* This routine, and its companion below, clips a line defined by its */ /* endpoints to either above some line y=c, or below some line y=c. By */ /* passing in parameters in different orders, we can clip to vertical */ /* lines, too. These are used by the DrawClip() routine below. */ void ClipLesser(x1, y1, x2, y2, s) int *x1, *y1, *x2, *y2, s; { *x1 -= (int)((long)(*y1-s)*(*x2-*x1)/(*y2-*y1)); *y1 = s; } void ClipGreater(x1, y1, x2, y2, s) int *x1, *y1, *x2, *y2, s; { *x1 += (int)((long)(s-*y1)*(*x2-*x1)/(*y2-*y1)); *y1 = s; } /* Draw a line on the screen. This is just like DrawLine() routine earlier; */ /* however, first clip the endpoints to the window viewport before drawing. */ void DrawClip(x1, y1, x2, y2, xl, yl, xh, yh, skip) int x1, y1, x2, y2, xl, yl, xh, yh, skip; { if (x1 < xl) ClipLesser (&y1, &x1, &y2, &x2, xl); /* Check left side of window. */ if (x2 < xl) ClipLesser (&y2, &x2, &y1, &x1, xl); if (y1 < yl) ClipLesser (&x1, &y1, &x2, &y2, yl); /* Check top side of window. */ if (y2 < yl) ClipLesser (&x2, &y2, &x1, &y1, yl); if (x1 > xh) ClipGreater(&y1, &x1, &y2, &x2, xh); /* Check right of window. */ if (x2 > xh) ClipGreater(&y2, &x2, &y1, &x1, xh); if (y1 > yh) ClipGreater(&x1, &y1, &x2, &y2, yh); /* Check bottom of window. */ if (y2 > yh) ClipGreater(&x2, &y2, &x1, &y1, yh); DrawDash(x1, y1, x2, y2, skip); /* Go draw the line. */ } /* Draw a circle or ellipse inside the given bounding rectangle. */ void DrawEllipse(x1, y1, x2, y2) int x1, y1, x2, y2; { int x, y, rx, ry, m, n, u, v, i; if (xfile) { x = (x1+x2)/2; y = (y1+y2)/2; rx = (x2-x1)/2; ry = (y2-y1)/2; if (xbitmap) { InitCircle(); m = x + rx; n = y; for (i = 0; i <= DEGD; i += DEGINC) { u = x + (int)((real)rx*circ->x[i]); v = y + (int)((real)ry*circ->y[i]); u = MIN(u, x + rx-1); v = MIN(v, y + ry-1); DrawLine(m, n, u, v); m = u; n = v; } } #ifdef PS else if (psfile) { PSlinecap(FALSE); PSforcestroke(); PSdash(0); fprintf(psdata, "%d %d %d %d el\n", rx, ry, x, y); } #endif #ifdef META else { metafilldes = 16; /* Specify a hollow fill brush. */ MetaSelect(); MetaEllipse(x1+metawid/3, y1+metawid/3, x2+metawid/3, y2+metawid/3); } #endif } #ifdef X11 else XDrawArc(disp, pixmap, gc, x1, y1, x2-x1, y2-y1, 0, 360*64); #endif #ifdef MSG else _ellipse(_GBORDER, offsetx + x1, offsety + y1, offsetx + x2, offsety + y2); #endif } /* Print a string of text on the graphic window at specified location. To */ /* do this we either use Astrolog's own "font" (6x10) and draw each letter */ /* separately, or else specify system fonts for PostScript and metafiles. */ void DrawText(string, x, y, base) char *string; int x, y, base; { int s = scale, c = colcur, len; len = StringLen(string); scale = 100 * scalet; x += SCALE; if (base >= FALSE) x -= len*FONTX*SCALE/2; if (!base) y -= FONTY*SCALE/2; else y -= (FONTY-3)*SCALE; DrawColor(off); DrawBlock(x, y, x+FONTX*SCALE*len, y+(FONTY-1)*SCALE); DrawColor(c); #ifdef PS if (psfile && xfont) { PSfont(4); fprintf(psdata, "%d %d(%s)center\n", x + FONTX*SCALE*len/2, y + FONTY*SCALE/2, string); scale = s; return; } #endif while (*string) { #ifdef META if (metafile && xfont) { metafontdes = 3; metatxtcdes = colcur; metatxtades = 0x6 | 0 /* Center | Top */; MetaSelect(); MetaTextOut(x, y, 1); MetaWord(MAKEWORD(*string, 0)); } else #endif DrawTurtle(asciidraw[*string-' '], x, y); x += FONTX*SCALE; string++; } scale = s; } /* Draw the glyph of a sign at particular coordinates on the screen. */ /* To do this we either use Astrolog's turtle vector representation or */ /* we may specify a system font character for PostScript and metafiles. */ void DrawSign(i, x, y) int i, x, y; { #ifdef PS if (psfile && xfont) { PSfont(1); fprintf(psdata, "%d %d(%c)center\n", x, y, 'A' + i - 1); return; } #endif #ifdef META if (metafile && xfont) { metafontdes = 1; metatxtcdes = colcur; metatxtades = 0x6 | 0x8 /* Center | Bottom */; MetaSelect(); MetaTextOut(x, y+4*SCALE, 1); MetaWord(MAKEWORD('^' + i - 1, 0)); return; } #endif DrawTurtle(signdraw[i], x, y); } /* Draw the number of a house at particular coordinates on the screen. */ /* We either use a turtle vector or write a number in a system font. */ void DrawHouse(i, x, y) int i, x, y; { #ifdef PS if (psfile && xfont) { PSfont(3); fprintf(psdata, "%d %d(%d)center\n", x, y, i); return; } #endif #ifdef META if (metafile && xfont) { metafontdes = 2; metatxtcdes = colcur; metatxtades = 0x6 | 0x8 /* Center | Bottom */; MetaSelect(); MetaTextOut(x, y+3*SCALE, 1 + (i>9)); MetaWord(MAKEWORD(i > 9 ? '1' : '0'+i, i > 9 ? '0'+i-10 : 0)); return; } #endif DrawTurtle(housedraw[i], x, y); } /* Draw the glyph of an object at particular coordinates on the screen. */ void DrawObject(i, x, y) int i, x, y; { char glyph[4]; #ifdef PS static char objectchar[] = "dQRSTUVWXYZ < ba "; #endif #ifdef META char c = 0; #endif if (!xlabel) /* If we are inhibiting labels, then do nothing. */ return; /* For other planet centered charts, we have to remember that that */ /* particular planet's index now represents the Earth. If we are given */ /* that index to draw, then change it so we draw the Earth instead. */ if (modex != MODES && ((i == centerplanet && i > _MOO) || (centerplanet == 0 && i == _SUN))) i = 0; DrawColor(objectcolor[i]); if (i <= BASE) { #ifdef PS if (psfile && xfont == 1 && i <= OBJECTS && objectchar[i] != ' ') { PSfont(2); fprintf(psdata, "%d %d(%c)center\n", x, y, objectchar[i]); return; } #endif #ifdef META if (metafile && xfont == 1) { if (i < _SUN) c = ';'; else if (i <= _PLU) c = 'Q' + i - 1; else if (i == _NOD) c = '<'; else if (i == _MC) c = 'b'; else if (i == _ASC) c = 'a'; } if (c) { metafontdes = 4; metatxtcdes = colcur; metatxtades = 0x6 | 0x8 /* Center | Bottom */; MetaSelect(); MetaTextOut(x, y+5*SCALE, 1); MetaWord(MAKEWORD(c, 0)); return; } #endif DrawTurtle(objectdraw[i], x, y); /* Normally we can just go draw the glyph; however, stars don't have */ /* glyphs, so for these draw their three letter abbreviation. */ } else { sprintf(glyph, "%c%c%c", OBJNAM(i)); DrawText(glyph, x, y, FALSE); } } /* Draw the glyph of an aspect at particular coordinates on the screen. */ /* Again we either use Astrolog's turtle vector or a system Astro font. */ void DrawAspect(i, x, y) int i, x, y; { #ifdef PS static char aspectchar[] = "!\"#$'&%()+- "; #endif #ifdef META char c = 0; #endif #ifdef PS if (psfile && xfont == 1 && aspectchar[i-1] != ' ') { PSfont(2); fprintf(psdata, "%d %d(%s%c)center\n", x, y, i == _SSQ || i == _SES ? "\\" : "", aspectchar[i-1]); return; } #endif #ifdef META if (metafile && xfont == 1) { if (i <= _TRI) c = '!' + i - 1; else if (i == _SEX) c = '\''; else if (i == _INC) c = '&'; else if (i == _SSX) c = '%'; else if (i == _SSQ) c = '('; else if (i == _SES) c = ')'; else if (i == _QUI) c = '+'; else if (i == _BQN) c = '-'; } if (c) { metafontdes = 4; metatxtcdes = colcur; metatxtades = 0x6 | 0x8 /* Center | Bottom */; MetaSelect(); MetaTextOut(x, y+5*SCALE, 1); MetaWord(MAKEWORD(c, 0)); return; } #endif DrawTurtle(aspectdraw[i], x, y); } /* Convert a string segment to a positive number, updating the string to */ /* point beyond the number chars. Return 1 if the string doesn't point to */ /* a numeric value. This is used by the DrawTurtle() routine to extract */ /* motion vector quantities from draw strings, e.g. the "12" in "U12". */ int IntInString(str) char **str; { int num = 0, i = 0; loop { if (**str < '0' || **str > '9') return num > 0 ? num : (i < 1 ? 1 : 0); num = num*10+(**str)-'0'; (*str)++; i++; } } /* This routine is used to draw complicated objects composed of lots of line */ /* segments on the screen, such as all the glyphs and coastline pieces. It */ /* is passed in a string of commands defining what to draw in relative */ /* coordinates. This is a copy of the format of the BASIC draw command found */ /* in PC's. For example, "U5R10D5L10" means go up 5 dots, right 10, down 5, */ /* and left 10 - draw a box twice as wide as it is high. */ void DrawTurtle(lin, x0, y0) char *lin; int x0, y0; { int i, j, x, y, deltax, deltay, blank, noupdate; char cmd; turtlex = x0; turtley = y0; while (cmd = CAP(*lin)) { lin++; /* 'B' prefixing a command means just move the cursor, and don't draw. */ if (blank = cmd == 'B') { cmd = CAP(*lin); lin++; } /* 'N' prefixing a command means don't update cursor when done drawing. */ if (noupdate = cmd == 'N') { cmd = CAP(*lin); lin++; } /* Here we process the eight directional commands. */ switch (cmd) { case 'U': deltax = 0; deltay = -1; break; /* Up */ case 'D': deltax = 0; deltay = 1; break; /* Down */ case 'L': deltax = -1; deltay = 0; break; /* Left */ case 'R': deltax = 1; deltay = 0; break; /* Right */ case 'E': deltax = 1; deltay = -1; break; /* NorthEast */ case 'F': deltax = 1; deltay = 1; break; /* SouthEast */ case 'G': deltax = -1; deltay = 1; break; /* SouthWest */ case 'H': deltax = -1; deltay = -1; break; /* NorthWest */ default: PrintError("Bad turtle subcommand."); /* Shouldn't happen. */ } x = turtlex; y = turtley; j = IntInString(&lin)*SCALE; /* Figure out how far to draw. */ if (blank) { turtlex += deltax*j; turtley += deltay*j; } else { if (psfile || metafile) { turtlex += deltax*j; turtley += deltay*j; DrawLine(x, y, turtlex, turtley); } else { DrawPoint(turtlex, turtley); for (i = 0; i < j; i++) { turtlex += deltax; turtley += deltay; DrawPoint(turtlex, turtley); } } if (noupdate) { turtlex = x; turtley = y; } } } } #endif /* GRAPH */ /* xgeneral.c */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.