|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <draw.h> | 
|  | #include <event.h> | 
|  | #include <ctype.h> | 
|  | #include "map.h" | 
|  | #undef	RAD | 
|  | #undef	TWOPI | 
|  | #include "sky.h" | 
|  |  | 
|  | /* convert to milliarcsec */ | 
|  | static int32	c = MILLIARCSEC;	/* 1 degree */ | 
|  | static int32	m5 = 1250*60*60;	/* 5 minutes of ra */ | 
|  |  | 
|  | DAngle	ramin; | 
|  | DAngle	ramax; | 
|  | DAngle	decmin; | 
|  | DAngle	decmax; | 
|  | int		folded; | 
|  |  | 
|  | Image	*grey; | 
|  | Image	*alphagrey; | 
|  | Image	*green; | 
|  | Image	*lightblue; | 
|  | Image	*lightgrey; | 
|  | Image	*ocstipple; | 
|  | Image	*suncolor; | 
|  | Image	*mooncolor; | 
|  | Image	*shadowcolor; | 
|  | Image	*mercurycolor; | 
|  | Image	*venuscolor; | 
|  | Image	*marscolor; | 
|  | Image	*jupitercolor; | 
|  | Image	*saturncolor; | 
|  | Image	*uranuscolor; | 
|  | Image	*neptunecolor; | 
|  | Image	*plutocolor; | 
|  | Image	*cometcolor; | 
|  |  | 
|  | Planetrec	*planet; | 
|  |  | 
|  | int32	mapx0, mapy0; | 
|  | int32	mapra, mapdec; | 
|  | double	mylat, mylon, mysid; | 
|  | double	mapscale; | 
|  | double	maps; | 
|  | int (*projection)(struct place*, double*, double*); | 
|  |  | 
|  | char *fontname = "/lib/font/bit/luc/unicode.6.font"; | 
|  |  | 
|  | /* types Coord and Loc correspond to types in map(3) thus: | 
|  | Coord == struct coord; | 
|  | Loc == struct place, except longitudes are measured | 
|  | positive east for Loc and west for struct place | 
|  | */ | 
|  |  | 
|  | typedef struct Xyz Xyz; | 
|  | typedef struct coord Coord; | 
|  | typedef struct Loc Loc; | 
|  |  | 
|  | struct Xyz{ | 
|  | double x,y,z; | 
|  | }; | 
|  |  | 
|  | struct Loc{ | 
|  | Coord nlat, elon; | 
|  | }; | 
|  |  | 
|  | Xyz north = { 0, 0, 1 }; | 
|  |  | 
|  | int | 
|  | plotopen(void) | 
|  | { | 
|  | if(display != nil) | 
|  | return 1; | 
|  | if(initdraw(drawerror, nil, nil) < 0){ | 
|  | fprint(2, "initdisplay failed: %r\n"); | 
|  | return -1; | 
|  | } | 
|  | grey = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x777777FF); | 
|  | lightgrey = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0xAAAAAAFF); | 
|  | alphagrey = allocimage(display, Rect(0, 0, 2, 2), RGBA32, 1, 0x77777777); | 
|  | green = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x00AA00FF); | 
|  | lightblue = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x009EEEFF); | 
|  | ocstipple = allocimage(display, Rect(0, 0, 2, 2), CMAP8, 1, 0xAAAAAAFF); | 
|  | draw(ocstipple, Rect(0, 0, 1, 1), display->black, nil, ZP); | 
|  | draw(ocstipple, Rect(1, 1, 2, 2), display->black, nil, ZP); | 
|  |  | 
|  | suncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFFF77FF); | 
|  | mooncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAAAFF); | 
|  | shadowcolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x00000055); | 
|  | mercurycolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFAAAAFF); | 
|  | venuscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAFFFF); | 
|  | marscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFF5555FF); | 
|  | jupitercolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFFFAAFF); | 
|  | saturncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAFFAAFF); | 
|  | uranuscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x77DDDDFF); | 
|  | neptunecolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x77FF77FF); | 
|  | plutocolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x7777FFFF); | 
|  | cometcolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAFFFF); | 
|  | font = openfont(display, fontname); | 
|  | if(font == nil) | 
|  | fprint(2, "warning: no font %s: %r\n", fontname); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Function heavens() for setting up observer-eye-view | 
|  | * sky charts, plus subsidiary functions. | 
|  | * Written by Doug McIlroy. | 
|  | */ | 
|  |  | 
|  | /* heavens(nlato, wlono, nlatc, wlonc) sets up a map(3)-style | 
|  | coordinate change (by calling orient()) and returns a | 
|  | projection function heavens for calculating a star map | 
|  | centered on nlatc,wlonc and rotated so it will appear | 
|  | upright as seen by a ground observer at nlato,wlono. | 
|  | all coordinates (north latitude and west longitude) | 
|  | are in degrees. | 
|  | */ | 
|  |  | 
|  | Coord | 
|  | mkcoord(double degrees) | 
|  | { | 
|  | Coord c; | 
|  |  | 
|  | c.l = degrees*PI/180; | 
|  | c.s = sin(c.l); | 
|  | c.c = cos(c.l); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | Xyz | 
|  | ptov(struct place p) | 
|  | { | 
|  | Xyz v; | 
|  |  | 
|  | v.z = p.nlat.s; | 
|  | v.x = p.nlat.c * p.wlon.c; | 
|  | v.y = -p.nlat.c * p.wlon.s; | 
|  | return v; | 
|  | } | 
|  |  | 
|  | double | 
|  | dot(Xyz a, Xyz b) | 
|  | { | 
|  | return a.x*b.x + a.y*b.y + a.z*b.z; | 
|  | } | 
|  |  | 
|  | Xyz | 
|  | cross(Xyz a, Xyz b) | 
|  | { | 
|  | Xyz v; | 
|  |  | 
|  | v.x = a.y*b.z - a.z*b.y; | 
|  | v.y = a.z*b.x - a.x*b.z; | 
|  | v.z = a.x*b.y - a.y*b.x; | 
|  | return v; | 
|  | } | 
|  |  | 
|  | double | 
|  | len(Xyz a) | 
|  | { | 
|  | return sqrt(dot(a, a)); | 
|  | } | 
|  |  | 
|  | /* An azimuth vector from a to b is a tangent to | 
|  | the sphere at a pointing along the (shorter) | 
|  | great-circle direction to b.  It lies in the | 
|  | plane of the vectors a and b, is perpendicular | 
|  | to a, and has a positive compoent along b, | 
|  | provided a and b span a 2-space.  Otherwise | 
|  | it is null.  (aXb)Xa, where X denotes cross | 
|  | product, is such a vector. */ | 
|  |  | 
|  | Xyz | 
|  | azvec(Xyz a, Xyz b) | 
|  | { | 
|  | return cross(cross(a,b), a); | 
|  | } | 
|  |  | 
|  | /* Find the azimuth of point b as seen | 
|  | from point a, given that a is distinct | 
|  | from either pole and from b */ | 
|  |  | 
|  | double | 
|  | azimuth(Xyz a, Xyz b) | 
|  | { | 
|  | Xyz aton = azvec(a,north); | 
|  | Xyz atob = azvec(a,b); | 
|  | double lenaton = len(aton); | 
|  | double lenatob = len(atob); | 
|  | double az = acos(dot(aton,atob)/(lenaton*lenatob)); | 
|  |  | 
|  | if(dot(b,cross(north,a)) < 0) | 
|  | az = -az; | 
|  | return az; | 
|  | } | 
|  |  | 
|  | /* Find the rotation (3rd argument of orient() in the | 
|  | map projection package) for the map described | 
|  | below.  The return is radians; it must be converted | 
|  | to degrees for orient(). | 
|  | */ | 
|  |  | 
|  | double | 
|  | rot(struct place center, struct place zenith) | 
|  | { | 
|  | Xyz cen = ptov(center); | 
|  | Xyz zen = ptov(zenith); | 
|  |  | 
|  | if(cen.z > 1-FUZZ) 	/* center at N pole */ | 
|  | return PI + zenith.wlon.l; | 
|  | if(cen.z < FUZZ-1)		/* at S pole */ | 
|  | return -zenith.wlon.l; | 
|  | if(fabs(dot(cen,zen)) > 1-FUZZ)	/* at zenith */ | 
|  | return 0; | 
|  | return azimuth(cen, zen); | 
|  | } | 
|  |  | 
|  | int (* | 
|  | heavens(double zlatdeg, double zlondeg, double clatdeg, double clondeg))(struct place*, double*, double*) | 
|  | { | 
|  | struct place center; | 
|  | struct place zenith; | 
|  |  | 
|  | center.nlat = mkcoord(clatdeg); | 
|  | center.wlon = mkcoord(clondeg); | 
|  | zenith.nlat = mkcoord(zlatdeg); | 
|  | zenith.wlon = mkcoord(zlondeg); | 
|  | projection = stereographic(); | 
|  | orient(clatdeg, clondeg, rot(center, zenith)*180/PI); | 
|  | return projection; | 
|  | } | 
|  |  | 
|  | int | 
|  | maptoxy(int32 ra, int32 dec, double *x, double *y) | 
|  | { | 
|  | double lat, lon; | 
|  | struct place pl; | 
|  |  | 
|  | lat = angle(dec); | 
|  | lon = angle(ra); | 
|  | pl.nlat.l = lat; | 
|  | pl.nlat.s = sin(lat); | 
|  | pl.nlat.c = cos(lat); | 
|  | pl.wlon.l = lon; | 
|  | pl.wlon.s = sin(lon); | 
|  | pl.wlon.c = cos(lon); | 
|  | normalize(&pl); | 
|  | return projection(&pl, x, y); | 
|  | } | 
|  |  | 
|  | /* end of 'heavens' section */ | 
|  |  | 
|  | int | 
|  | setmap(int32 ramin, int32 ramax, int32 decmin, int32 decmax, Rectangle r, int zenithup) | 
|  | { | 
|  | int c; | 
|  | double minx, maxx, miny, maxy; | 
|  |  | 
|  | c = 1000*60*60; | 
|  | mapra = ramax/2+ramin/2; | 
|  | mapdec = decmax/2+decmin/2; | 
|  |  | 
|  | if(zenithup) | 
|  | projection = heavens(mylat, mysid, (double)mapdec/c, (double)mapra/c); | 
|  | else{ | 
|  | orient((double)mapdec/c, (double)mapra/c, 0.); | 
|  | projection = stereographic(); | 
|  | } | 
|  | mapx0 = (r.max.x+r.min.x)/2; | 
|  | mapy0 = (r.max.y+r.min.y)/2; | 
|  | maps = ((double)Dy(r))/(double)(decmax-decmin); | 
|  | if(maptoxy(ramin, decmin, &minx, &miny) < 0) | 
|  | return -1; | 
|  | if(maptoxy(ramax, decmax, &maxx, &maxy) < 0) | 
|  | return -1; | 
|  | /* | 
|  | * It's tricky to get the scale right.  This noble attempt scales | 
|  | * to fit the lengths of the sides of the rectangle. | 
|  | */ | 
|  | mapscale = Dx(r)/(minx-maxx); | 
|  | if(mapscale > Dy(r)/(maxy-miny)) | 
|  | mapscale = Dy(r)/(maxy-miny); | 
|  | /* | 
|  | * near the pole It's not a rectangle, though, so this scales | 
|  | * to fit the corners of the trapezoid (a triangle at the pole). | 
|  | */ | 
|  | mapscale *= (cos(angle(mapdec))+1.)/2; | 
|  | if(maxy  < miny){ | 
|  | /* upside down, between zenith and pole */ | 
|  | fprint(2, "reverse plot\n"); | 
|  | mapscale = -mapscale; | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | Point | 
|  | map(int32 ra, int32 dec) | 
|  | { | 
|  | double x, y; | 
|  | Point p; | 
|  |  | 
|  | if(maptoxy(ra, dec, &x, &y) > 0){ | 
|  | p.x = mapscale*x + mapx0; | 
|  | p.y = mapscale*-y + mapy0; | 
|  | }else{ | 
|  | p.x = -100; | 
|  | p.y = -100; | 
|  | } | 
|  | return p; | 
|  | } | 
|  |  | 
|  | int | 
|  | dsize(int mag)	/* mag is 10*magnitude; return disc size */ | 
|  | { | 
|  | double d; | 
|  |  | 
|  | mag += 25;	/* make mags all positive; sirius is -1.6m */ | 
|  | d = (130-mag)/10; | 
|  | /* if plate scale is huge, adjust */ | 
|  | if(maps < 100.0/MILLIARCSEC) | 
|  | d *= .71; | 
|  | if(maps < 50.0/MILLIARCSEC) | 
|  | d *= .71; | 
|  | return d; | 
|  | } | 
|  |  | 
|  | void | 
|  | drawname(Image *scr, Image *col, char *s, int ra, int dec) | 
|  | { | 
|  | Point p; | 
|  |  | 
|  | if(font == nil) | 
|  | return; | 
|  | p = addpt(map(ra, dec), Pt(4, -1));	/* font has huge ascent */ | 
|  | string(scr, p, col, ZP, font, s); | 
|  | } | 
|  |  | 
|  | int | 
|  | npixels(DAngle diam) | 
|  | { | 
|  | Point p0, p1; | 
|  |  | 
|  | diam = DEG(angle(diam)*MILLIARCSEC);	/* diam in milliarcsec */ | 
|  | diam /= 2;	/* radius */ | 
|  | /* convert to plate scale */ | 
|  | /* BUG: need +100 because we sometimes crash in library if we plot the exact center */ | 
|  | p0 = map(mapra+100, mapdec); | 
|  | p1 = map(mapra+100+diam, mapdec); | 
|  | return hypot(p0.x-p1.x, p0.y-p1.y); | 
|  | } | 
|  |  | 
|  | void | 
|  | drawdisc(Image *scr, DAngle semidiam, int ring, Image *color, Point pt, char *s) | 
|  | { | 
|  | int d; | 
|  |  | 
|  | d = npixels(semidiam*2); | 
|  | if(d < 5) | 
|  | d = 5; | 
|  | fillellipse(scr, pt, d, d, color, ZP); | 
|  | if(ring == 1) | 
|  | ellipse(scr, pt, 5*d/2, d/2, 0, color, ZP); | 
|  | if(ring == 2) | 
|  | ellipse(scr, pt, d, d, 0, grey, ZP); | 
|  | if(s){ | 
|  | d = stringwidth(font, s); | 
|  | pt.x -= d/2; | 
|  | pt.y -= font->height/2 + 1; | 
|  | string(scr, pt, display->black, ZP, font, s); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | drawplanet(Image *scr, Planetrec *p, Point pt) | 
|  | { | 
|  | if(strcmp(p->name, "sun") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, suncolor, pt, nil); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "moon") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, mooncolor, pt, nil); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "shadow") == 0){ | 
|  | drawdisc(scr, p->semidiam, 2, shadowcolor, pt, nil); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "mercury") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, mercurycolor, pt, "m"); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "venus") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, venuscolor, pt, "v"); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "mars") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, marscolor, pt, "M"); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "jupiter") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, jupitercolor, pt, "J"); | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "saturn") == 0){ | 
|  | drawdisc(scr, p->semidiam, 1, saturncolor, pt, "S"); | 
|  |  | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "uranus") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, uranuscolor, pt, "U"); | 
|  |  | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "neptune") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, neptunecolor, pt, "N"); | 
|  |  | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "pluto") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, plutocolor, pt, "P"); | 
|  |  | 
|  | return; | 
|  | } | 
|  | if(strcmp(p->name, "comet") == 0){ | 
|  | drawdisc(scr, p->semidiam, 0, cometcolor, pt, "C"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | pt.x -= stringwidth(font, p->name)/2; | 
|  | pt.y -= font->height/2; | 
|  | string(scr, pt, grey, ZP, font, p->name); | 
|  | } | 
|  |  | 
|  | void | 
|  | tolast(char *name) | 
|  | { | 
|  | int i, nlast; | 
|  | Record *r, rr; | 
|  |  | 
|  | /* stop early to simplify inner loop adjustment */ | 
|  | nlast = 0; | 
|  | for(i=0,r=rec; i<nrec-nlast; i++,r++) | 
|  | if(r->type == Planet) | 
|  | if(name==nil || strcmp(r->u.planet.name, name)==0){ | 
|  | rr = *r; | 
|  | memmove(rec+i, rec+i+1, (nrec-i-1)*sizeof(Record)); | 
|  | rec[nrec-1] = rr; | 
|  | --i; | 
|  | --r; | 
|  | nlast++; | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | bbox(int32 extrara, int32 extradec, int quantize) | 
|  | { | 
|  | int i; | 
|  | Record *r; | 
|  | int ra, dec; | 
|  | int rah, ram, d1, d2; | 
|  | double r0; | 
|  |  | 
|  | ramin = 0x7FFFFFFF; | 
|  | ramax = -0x7FFFFFFF; | 
|  | decmin = 0x7FFFFFFF; | 
|  | decmax = -0x7FFFFFFF; | 
|  |  | 
|  | for(i=0,r=rec; i<nrec; i++,r++){ | 
|  | if(r->type == Patch){ | 
|  | radec(r->index, &rah, &ram, &dec); | 
|  | ra = 15*rah+ram/4; | 
|  | r0 = c/cos(dec*PI/180); | 
|  | ra *= c; | 
|  | dec *= c; | 
|  | if(dec == 0) | 
|  | d1 = c, d2 = c; | 
|  | else if(dec < 0) | 
|  | d1 = c, d2 = 0; | 
|  | else | 
|  | d1 = 0, d2 = c; | 
|  | }else if(r->type==SAO || r->type==NGC || r->type==Planet || r->type==Abell){ | 
|  | ra = r->u.ngc.ra; | 
|  | dec = r->u.ngc.dec; | 
|  | d1 = 0, d2 = 0, r0 = 0; | 
|  | }else | 
|  | continue; | 
|  | if(dec+d2+extradec > decmax) | 
|  | decmax = dec+d2+extradec; | 
|  | if(dec-d1-extradec < decmin) | 
|  | decmin = dec-d1-extradec; | 
|  | if(folded){ | 
|  | ra -= 180*c; | 
|  | if(ra < 0) | 
|  | ra += 360*c; | 
|  | } | 
|  | if(ra+r0+extrara > ramax) | 
|  | ramax = ra+r0+extrara; | 
|  | if(ra-extrara < ramin) | 
|  | ramin = ra-extrara; | 
|  | } | 
|  |  | 
|  | if(decmax > 90*c) | 
|  | decmax = 90*c; | 
|  | if(decmin < -90*c) | 
|  | decmin = -90*c; | 
|  | if(ramin < 0) | 
|  | ramin += 360*c; | 
|  | if(ramax >= 360*c) | 
|  | ramax -= 360*c; | 
|  |  | 
|  | if(quantize){ | 
|  | /* quantize to degree boundaries */ | 
|  | ramin -= ramin%m5; | 
|  | if(ramax%m5 != 0) | 
|  | ramax += m5-(ramax%m5); | 
|  | if(decmin > 0) | 
|  | decmin -= decmin%c; | 
|  | else | 
|  | decmin -= c - (-decmin)%c; | 
|  | if(decmax > 0){ | 
|  | if(decmax%c != 0) | 
|  | decmax += c-(decmax%c); | 
|  | }else if(decmax < 0){ | 
|  | if(decmax%c != 0) | 
|  | decmax += ((-decmax)%c); | 
|  | } | 
|  | } | 
|  |  | 
|  | if(folded){ | 
|  | if(ramax-ramin > 270*c){ | 
|  | fprint(2, "ra range too wide %ld°\n", (ramax-ramin)/c); | 
|  | return -1; | 
|  | } | 
|  | }else if(ramax-ramin > 270*c){ | 
|  | folded = 1; | 
|  | return bbox(0, 0, quantize); | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | inbbox(DAngle ra, DAngle dec) | 
|  | { | 
|  | int min; | 
|  |  | 
|  | if(dec < decmin) | 
|  | return 0; | 
|  | if(dec > decmax) | 
|  | return 0; | 
|  | min = ramin; | 
|  | if(ramin > ramax){	/* straddling 0h0m */ | 
|  | min -= 360*c; | 
|  | if(ra > 180*c) | 
|  | ra -= 360*c; | 
|  | } | 
|  | if(ra < min) | 
|  | return 0; | 
|  | if(ra > ramax) | 
|  | return 0; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | int | 
|  | gridra(int32 mapdec) | 
|  | { | 
|  | mapdec = abs(mapdec)+c/2; | 
|  | mapdec /= c; | 
|  | if(mapdec < 60) | 
|  | return m5; | 
|  | if(mapdec < 80) | 
|  | return 2*m5; | 
|  | if(mapdec < 85) | 
|  | return 4*m5; | 
|  | return 8*m5; | 
|  | } | 
|  |  | 
|  | #define	GREY	(nogrey? display->white : grey) | 
|  |  | 
|  | void | 
|  | plot(char *flags) | 
|  | { | 
|  | int i, j, k; | 
|  | char *t; | 
|  | int32 x, y; | 
|  | int ra, dec; | 
|  | int m; | 
|  | Point p, pts[10]; | 
|  | Record *r; | 
|  | Rectangle rect, r1; | 
|  | int dx, dy, nogrid, textlevel, nogrey, zenithup; | 
|  | Image *scr; | 
|  | char *name, buf[32]; | 
|  | double v; | 
|  |  | 
|  | if(plotopen() < 0) | 
|  | return; | 
|  | nogrid = 0; | 
|  | nogrey = 0; | 
|  | textlevel = 1; | 
|  | dx = 512; | 
|  | dy = 512; | 
|  | zenithup = 0; | 
|  | for(;;){ | 
|  | if(t = alpha(flags, "nogrid")){ | 
|  | nogrid = 1; | 
|  | flags = t; | 
|  | continue; | 
|  | } | 
|  | if((t = alpha(flags, "zenith")) || (t = alpha(flags, "zenithup")) ){ | 
|  | zenithup = 1; | 
|  | flags = t; | 
|  | continue; | 
|  | } | 
|  | if((t = alpha(flags, "notext")) || (t = alpha(flags, "nolabel")) ){ | 
|  | textlevel = 0; | 
|  | flags = t; | 
|  | continue; | 
|  | } | 
|  | if((t = alpha(flags, "alltext")) || (t = alpha(flags, "alllabel")) ){ | 
|  | textlevel = 2; | 
|  | flags = t; | 
|  | continue; | 
|  | } | 
|  | if(t = alpha(flags, "dx")){ | 
|  | dx = strtol(t, &t, 0); | 
|  | if(dx < 100){ | 
|  | fprint(2, "dx %d too small (min 100) in plot\n", dx); | 
|  | return; | 
|  | } | 
|  | flags = skipbl(t); | 
|  | continue; | 
|  | } | 
|  | if(t = alpha(flags, "dy")){ | 
|  | dy = strtol(t, &t, 0); | 
|  | if(dy < 100){ | 
|  | fprint(2, "dy %d too small (min 100) in plot\n", dy); | 
|  | return; | 
|  | } | 
|  | flags = skipbl(t); | 
|  | continue; | 
|  | } | 
|  | if((t = alpha(flags, "nogrey")) || (t = alpha(flags, "nogray"))){ | 
|  | nogrey = 1; | 
|  | flags = skipbl(t); | 
|  | continue; | 
|  | } | 
|  | if(*flags){ | 
|  | fprint(2, "syntax error in plot\n"); | 
|  | return; | 
|  | } | 
|  | break; | 
|  | } | 
|  | flatten(); | 
|  | folded = 0; | 
|  |  | 
|  | if(bbox(0, 0, 1) < 0) | 
|  | return; | 
|  | if(ramax-ramin<100 || decmax-decmin<100){ | 
|  | fprint(2, "plot too small\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | scr = allocimage(display, Rect(0, 0, dx, dy), RGB24, 0, DBlack); | 
|  | if(scr == nil){ | 
|  | fprint(2, "can't allocate image: %r\n"); | 
|  | return; | 
|  | } | 
|  | rect = scr->r; | 
|  | rect.min.x += 16; | 
|  | rect = insetrect(rect, 40); | 
|  | if(setmap(ramin, ramax, decmin, decmax, rect, zenithup) < 0){ | 
|  | fprint(2, "can't set up map coordinates\n"); | 
|  | return; | 
|  | } | 
|  | if(!nogrid){ | 
|  | for(x=ramin; ; ){ | 
|  | for(j=0; j<nelem(pts); j++){ | 
|  | /* use double to avoid overflow */ | 
|  | v = (double)j / (double)(nelem(pts)-1); | 
|  | v = decmin + v*(decmax-decmin); | 
|  | pts[j] = map(x, v); | 
|  | } | 
|  | bezspline(scr, pts, nelem(pts), Endsquare, Endsquare, 0, GREY, ZP); | 
|  | ra = x; | 
|  | if(folded){ | 
|  | ra -= 180*c; | 
|  | if(ra < 0) | 
|  | ra += 360*c; | 
|  | } | 
|  | p = pts[0]; | 
|  | p.x -= stringwidth(font, hm5(angle(ra)))/2; | 
|  | string(scr, p, GREY, ZP, font, hm5(angle(ra))); | 
|  | p = pts[nelem(pts)-1]; | 
|  | p.x -= stringwidth(font, hm5(angle(ra)))/2; | 
|  | p.y -= font->height; | 
|  | string(scr, p, GREY, ZP, font, hm5(angle(ra))); | 
|  | if(x == ramax) | 
|  | break; | 
|  | x += gridra(mapdec); | 
|  | if(x > ramax) | 
|  | x = ramax; | 
|  | } | 
|  | for(y=decmin; y<=decmax; y+=c){ | 
|  | for(j=0; j<nelem(pts); j++){ | 
|  | /* use double to avoid overflow */ | 
|  | v = (double)j / (double)(nelem(pts)-1); | 
|  | v = ramin + v*(ramax-ramin); | 
|  | pts[j] = map(v, y); | 
|  | } | 
|  | bezspline(scr, pts, nelem(pts), Endsquare, Endsquare, 0, GREY, ZP); | 
|  | p = pts[0]; | 
|  | p.x += 3; | 
|  | p.y -= font->height/2; | 
|  | string(scr, p, GREY, ZP, font, deg(angle(y))); | 
|  | p = pts[nelem(pts)-1]; | 
|  | p.x -= 3+stringwidth(font, deg(angle(y))); | 
|  | p.y -= font->height/2; | 
|  | string(scr, p, GREY, ZP, font, deg(angle(y))); | 
|  | } | 
|  | } | 
|  | /* reorder to get planets in front of stars */ | 
|  | tolast(nil); | 
|  | tolast("moon");		/* moon is in front of everything... */ | 
|  | tolast("shadow");	/* ... except the shadow */ | 
|  |  | 
|  | for(i=0,r=rec; i<nrec; i++,r++){ | 
|  | dec = r->u.ngc.dec; | 
|  | ra = r->u.ngc.ra; | 
|  | if(folded){ | 
|  | ra -= 180*c; | 
|  | if(ra < 0) | 
|  | ra += 360*c; | 
|  | } | 
|  | if(textlevel){ | 
|  | name = nameof(r); | 
|  | if(name==nil && textlevel>1 && r->type==SAO){ | 
|  | snprint(buf, sizeof buf, "SAO%ld", r->index); | 
|  | name = buf; | 
|  | } | 
|  | if(name) | 
|  | drawname(scr, nogrey? display->white : alphagrey, name, ra, dec); | 
|  | } | 
|  | if(r->type == Planet){ | 
|  | drawplanet(scr, &r->u.planet, map(ra, dec)); | 
|  | continue; | 
|  | } | 
|  | if(r->type == SAO){ | 
|  | m = r->u.sao.mag; | 
|  | if(m == UNKNOWNMAG) | 
|  | m = r->u.sao.mpg; | 
|  | if(m == UNKNOWNMAG) | 
|  | continue; | 
|  | m = dsize(m); | 
|  | if(m < 3) | 
|  | fillellipse(scr, map(ra, dec), m, m, nogrey? display->white : lightgrey, ZP); | 
|  | else{ | 
|  | ellipse(scr, map(ra, dec), m+1, m+1, 0, display->black, ZP); | 
|  | fillellipse(scr, map(ra, dec), m, m, display->white, ZP); | 
|  | } | 
|  | continue; | 
|  | } | 
|  | if(r->type == Abell){ | 
|  | ellipse(scr, addpt(map(ra, dec), Pt(-3, 2)), 2, 1, 0, lightblue, ZP); | 
|  | ellipse(scr, addpt(map(ra, dec), Pt(3, 2)), 2, 1, 0, lightblue, ZP); | 
|  | ellipse(scr, addpt(map(ra, dec), Pt(0, -2)), 1, 2, 0, lightblue, ZP); | 
|  | continue; | 
|  | } | 
|  | switch(r->u.ngc.type){ | 
|  | case Galaxy: | 
|  | j = npixels(r->u.ngc.diam); | 
|  | if(j < 4) | 
|  | j = 4; | 
|  | if(j > 10) | 
|  | k = j/3; | 
|  | else | 
|  | k = j/2; | 
|  | ellipse(scr, map(ra, dec), j, k, 0, lightblue, ZP); | 
|  | break; | 
|  |  | 
|  | case PlanetaryN: | 
|  | p = map(ra, dec); | 
|  | j = npixels(r->u.ngc.diam); | 
|  | if(j < 3) | 
|  | j = 3; | 
|  | ellipse(scr, p, j, j, 0, green, ZP); | 
|  | line(scr, Pt(p.x, p.y+j+1), Pt(p.x, p.y+j+4), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | line(scr, Pt(p.x, p.y-(j+1)), Pt(p.x, p.y-(j+4)), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | line(scr, Pt(p.x+j+1, p.y), Pt(p.x+j+4, p.y), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | line(scr, Pt(p.x-(j+1), p.y), Pt(p.x-(j+4), p.y), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | break; | 
|  |  | 
|  | case DiffuseN: | 
|  | case NebularCl: | 
|  | p = map(ra, dec); | 
|  | j = npixels(r->u.ngc.diam); | 
|  | if(j < 4) | 
|  | j = 4; | 
|  | r1.min = Pt(p.x-j, p.y-j); | 
|  | r1.max = Pt(p.x+j, p.y+j); | 
|  | if(r->u.ngc.type != DiffuseN) | 
|  | draw(scr, r1, ocstipple, ocstipple, ZP); | 
|  | line(scr, Pt(p.x-j, p.y-j), Pt(p.x+j, p.y-j), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | line(scr, Pt(p.x-j, p.y+j), Pt(p.x+j, p.y+j), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | line(scr, Pt(p.x-j, p.y-j), Pt(p.x-j, p.y+j), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | line(scr, Pt(p.x+j, p.y-j), Pt(p.x+j, p.y+j), | 
|  | Endsquare, Endsquare, 0, green, ZP); | 
|  | break; | 
|  |  | 
|  | case OpenCl: | 
|  | p = map(ra, dec); | 
|  | j = npixels(r->u.ngc.diam); | 
|  | if(j < 4) | 
|  | j = 4; | 
|  | fillellipse(scr, p, j, j, ocstipple, ZP); | 
|  | break; | 
|  |  | 
|  | case GlobularCl: | 
|  | j = npixels(r->u.ngc.diam); | 
|  | if(j < 4) | 
|  | j = 4; | 
|  | p = map(ra, dec); | 
|  | ellipse(scr, p, j, j, 0, lightgrey, ZP); | 
|  | line(scr, Pt(p.x-(j-1), p.y), Pt(p.x+j, p.y), | 
|  | Endsquare, Endsquare, 0, lightgrey, ZP); | 
|  | line(scr, Pt(p.x, p.y-(j-1)), Pt(p.x, p.y+j), | 
|  | Endsquare, Endsquare, 0, lightgrey, ZP); | 
|  | break; | 
|  |  | 
|  | } | 
|  | } | 
|  | flushimage(display, 1); | 
|  | displayimage(scr); | 
|  | } | 
|  |  | 
|  | int | 
|  | runcommand(char *command, int p[2]) | 
|  | { | 
|  | switch(rfork(RFPROC|RFFDG|RFNOWAIT)){ | 
|  | case -1: | 
|  | return -1; | 
|  | default: | 
|  | break; | 
|  | case 0: | 
|  | dup(p[1], 1); | 
|  | close(p[0]); | 
|  | close(p[1]); | 
|  | execlp("rc", "rc", "-c", command, nil); | 
|  | fprint(2, "can't exec %s: %r", command); | 
|  | exits(nil); | 
|  | } | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | void | 
|  | parseplanet(char *line, Planetrec *p) | 
|  | { | 
|  | char *fld[6]; | 
|  | int i, nfld; | 
|  | char *s; | 
|  |  | 
|  | if(line[0] == '\0') | 
|  | return; | 
|  | line[10] = '\0';	/* terminate name */ | 
|  | s = strrchr(line, ' '); | 
|  | if(s == nil) | 
|  | s = line; | 
|  | else | 
|  | s++; | 
|  | strcpy(p->name, s); | 
|  | for(i=0; s[i]!='\0'; i++) | 
|  | p->name[i] = tolower(s[i]); | 
|  | p->name[i] = '\0'; | 
|  | nfld = getfields(line+11, fld, nelem(fld), 1, " "); | 
|  | p->ra = dangle(getra(fld[0])); | 
|  | p->dec = dangle(getra(fld[1])); | 
|  | p->az = atof(fld[2])*MILLIARCSEC; | 
|  | p->alt = atof(fld[3])*MILLIARCSEC; | 
|  | p->semidiam = atof(fld[4])*1000; | 
|  | if(nfld > 5) | 
|  | p->phase = atof(fld[5]); | 
|  | else | 
|  | p->phase = 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | astro(char *flags, int initial) | 
|  | { | 
|  | int p[2]; | 
|  | int i, n, np; | 
|  | char cmd[256], buf[4096], *lines[20], *fld[10]; | 
|  |  | 
|  | snprint(cmd, sizeof cmd, "astro -p %s", flags); | 
|  | if(pipe(p) < 0){ | 
|  | fprint(2, "can't pipe: %r\n"); | 
|  | return; | 
|  | } | 
|  | if(runcommand(cmd, p) < 0){ | 
|  | close(p[0]); | 
|  | close(p[1]); | 
|  | fprint(2, "can't run astro: %r"); | 
|  | return; | 
|  | } | 
|  | close(p[1]); | 
|  | n = readn(p[0], buf, sizeof buf-1); | 
|  | if(n <= 0){ | 
|  | fprint(2, "no data from astro\n"); | 
|  | return; | 
|  | } | 
|  | if(!initial) | 
|  | Bwrite(&bout, buf, n); | 
|  | buf[n] = '\0'; | 
|  | np = getfields(buf, lines, nelem(lines), 0, "\n"); | 
|  | if(np <= 1){ | 
|  | fprint(2, "astro: not enough output\n"); | 
|  | return; | 
|  | } | 
|  | Bprint(&bout, "%s\n", lines[0]); | 
|  | Bflush(&bout); | 
|  | /* get latitude and longitude */ | 
|  | if(getfields(lines[0], fld, nelem(fld), 1, " ") < 8) | 
|  | fprint(2, "astro: can't read longitude: too few fields\n"); | 
|  | else{ | 
|  | mysid = getra(fld[5])*180./PI; | 
|  | mylat = getra(fld[6])*180./PI; | 
|  | mylon = getra(fld[7])*180./PI; | 
|  | } | 
|  | /* | 
|  | * Each time we run astro, we generate a new planet list | 
|  | * so multiple appearances of a planet may exist as we plot | 
|  | * its motion over time. | 
|  | */ | 
|  | planet = malloc(NPlanet*sizeof planet[0]); | 
|  | if(planet == nil){ | 
|  | fprint(2, "astro: malloc failed: %r\n"); | 
|  | exits("malloc"); | 
|  | } | 
|  | memset(planet, 0, NPlanet*sizeof planet[0]); | 
|  | for(i=1; i<np; i++) | 
|  | parseplanet(lines[i], &planet[i-1]); | 
|  | } |