| #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]); | 
 | } |