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.