plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

plot.c (21176B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include <draw.h>
      5 #include <event.h>
      6 #include <ctype.h>
      7 #include "map.h"
      8 #undef	RAD
      9 #undef	TWOPI
     10 #include "sky.h"
     11 
     12 	/* convert to milliarcsec */
     13 static int32	c = MILLIARCSEC;	/* 1 degree */
     14 static int32	m5 = 1250*60*60;	/* 5 minutes of ra */
     15 
     16 DAngle	ramin;
     17 DAngle	ramax;
     18 DAngle	decmin;
     19 DAngle	decmax;
     20 int		folded;
     21 
     22 Image	*grey;
     23 Image	*alphagrey;
     24 Image	*green;
     25 Image	*lightblue;
     26 Image	*lightgrey;
     27 Image	*ocstipple;
     28 Image	*suncolor;
     29 Image	*mooncolor;
     30 Image	*shadowcolor;
     31 Image	*mercurycolor;
     32 Image	*venuscolor;
     33 Image	*marscolor;
     34 Image	*jupitercolor;
     35 Image	*saturncolor;
     36 Image	*uranuscolor;
     37 Image	*neptunecolor;
     38 Image	*plutocolor;
     39 Image	*cometcolor;
     40 
     41 Planetrec	*planet;
     42 
     43 int32	mapx0, mapy0;
     44 int32	mapra, mapdec;
     45 double	mylat, mylon, mysid;
     46 double	mapscale;
     47 double	maps;
     48 int (*projection)(struct place*, double*, double*);
     49 
     50 char *fontname = "/lib/font/bit/luc/unicode.6.font";
     51 
     52 /* types Coord and Loc correspond to types in map(3) thus:
     53    Coord == struct coord;
     54    Loc == struct place, except longitudes are measured
     55      positive east for Loc and west for struct place
     56 */
     57 
     58 typedef struct Xyz Xyz;
     59 typedef struct coord Coord;
     60 typedef struct Loc Loc;
     61 
     62 struct Xyz{
     63 	double x,y,z;
     64 };
     65 
     66 struct Loc{
     67 	Coord nlat, elon;
     68 };
     69 
     70 Xyz north = { 0, 0, 1 };
     71 
     72 int
     73 plotopen(void)
     74 {
     75 	if(display != nil)
     76 		return 1;
     77 	if(initdraw(drawerror, nil, nil) < 0){
     78 		fprint(2, "initdisplay failed: %r\n");
     79 		return -1;
     80 	}
     81 	grey = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x777777FF);
     82 	lightgrey = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0xAAAAAAFF);
     83 	alphagrey = allocimage(display, Rect(0, 0, 2, 2), RGBA32, 1, 0x77777777);
     84 	green = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x00AA00FF);
     85 	lightblue = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x009EEEFF);
     86 	ocstipple = allocimage(display, Rect(0, 0, 2, 2), CMAP8, 1, 0xAAAAAAFF);
     87 	draw(ocstipple, Rect(0, 0, 1, 1), display->black, nil, ZP);
     88 	draw(ocstipple, Rect(1, 1, 2, 2), display->black, nil, ZP);
     89 
     90 	suncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFFF77FF);
     91 	mooncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAAAFF);
     92 	shadowcolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x00000055);
     93 	mercurycolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFAAAAFF);
     94 	venuscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAFFFF);
     95 	marscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFF5555FF);
     96 	jupitercolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFFFAAFF);
     97 	saturncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAFFAAFF);
     98 	uranuscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x77DDDDFF);
     99 	neptunecolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x77FF77FF);
    100 	plutocolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x7777FFFF);
    101 	cometcolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAFFFF);
    102 	font = openfont(display, fontname);
    103 	if(font == nil)
    104 		fprint(2, "warning: no font %s: %r\n", fontname);
    105 	return 1;
    106 }
    107 
    108 /*
    109  * Function heavens() for setting up observer-eye-view
    110  * sky charts, plus subsidiary functions.
    111  * Written by Doug McIlroy.
    112  */
    113 
    114 /* heavens(nlato, wlono, nlatc, wlonc) sets up a map(3)-style
    115    coordinate change (by calling orient()) and returns a
    116    projection function heavens for calculating a star map
    117    centered on nlatc,wlonc and rotated so it will appear
    118    upright as seen by a ground observer at nlato,wlono.
    119    all coordinates (north latitude and west longitude)
    120    are in degrees.
    121 */
    122 
    123 Coord
    124 mkcoord(double degrees)
    125 {
    126 	Coord c;
    127 
    128 	c.l = degrees*PI/180;
    129 	c.s = sin(c.l);
    130 	c.c = cos(c.l);
    131 	return c;
    132 }
    133 
    134 Xyz
    135 ptov(struct place p)
    136 {
    137 	Xyz v;
    138 
    139 	v.z = p.nlat.s;
    140 	v.x = p.nlat.c * p.wlon.c;
    141 	v.y = -p.nlat.c * p.wlon.s;
    142 	return v;
    143 }
    144 
    145 double
    146 dot(Xyz a, Xyz b)
    147 {
    148 	return a.x*b.x + a.y*b.y + a.z*b.z;
    149 }
    150 
    151 Xyz
    152 cross(Xyz a, Xyz b)
    153 {
    154 	Xyz v;
    155 
    156 	v.x = a.y*b.z - a.z*b.y;
    157 	v.y = a.z*b.x - a.x*b.z;
    158 	v.z = a.x*b.y - a.y*b.x;
    159 	return v;
    160 }
    161 
    162 double
    163 len(Xyz a)
    164 {
    165 	return sqrt(dot(a, a));
    166 }
    167 
    168 /* An azimuth vector from a to b is a tangent to
    169    the sphere at a pointing along the (shorter)
    170    great-circle direction to b.  It lies in the
    171    plane of the vectors a and b, is perpendicular
    172    to a, and has a positive compoent along b,
    173    provided a and b span a 2-space.  Otherwise
    174    it is null.  (aXb)Xa, where X denotes cross
    175    product, is such a vector. */
    176 
    177 Xyz
    178 azvec(Xyz a, Xyz b)
    179 {
    180 	return cross(cross(a,b), a);
    181 }
    182 
    183 /* Find the azimuth of point b as seen
    184    from point a, given that a is distinct
    185    from either pole and from b */
    186 
    187 double
    188 azimuth(Xyz a, Xyz b)
    189 {
    190 	Xyz aton = azvec(a,north);
    191 	Xyz atob = azvec(a,b);
    192 	double lenaton = len(aton);
    193 	double lenatob = len(atob);
    194 	double az = acos(dot(aton,atob)/(lenaton*lenatob));
    195 
    196 	if(dot(b,cross(north,a)) < 0)
    197 		az = -az;
    198 	return az;
    199 }
    200 
    201 /* Find the rotation (3rd argument of orient() in the
    202    map projection package) for the map described
    203    below.  The return is radians; it must be converted
    204    to degrees for orient().
    205 */
    206 
    207 double
    208 rot(struct place center, struct place zenith)
    209 {
    210 	Xyz cen = ptov(center);
    211 	Xyz zen = ptov(zenith);
    212 
    213 	if(cen.z > 1-FUZZ) 	/* center at N pole */
    214 		return PI + zenith.wlon.l;
    215 	if(cen.z < FUZZ-1)		/* at S pole */
    216 		return -zenith.wlon.l;
    217 	if(fabs(dot(cen,zen)) > 1-FUZZ)	/* at zenith */
    218 		return 0;
    219 	return azimuth(cen, zen);
    220 }
    221 
    222 int (*
    223 heavens(double zlatdeg, double zlondeg, double clatdeg, double clondeg))(struct place*, double*, double*)
    224 {
    225 	struct place center;
    226 	struct place zenith;
    227 
    228 	center.nlat = mkcoord(clatdeg);
    229 	center.wlon = mkcoord(clondeg);
    230 	zenith.nlat = mkcoord(zlatdeg);
    231 	zenith.wlon = mkcoord(zlondeg);
    232 	projection = stereographic();
    233 	orient(clatdeg, clondeg, rot(center, zenith)*180/PI);
    234 	return projection;
    235 }
    236 
    237 int
    238 maptoxy(int32 ra, int32 dec, double *x, double *y)
    239 {
    240 	double lat, lon;
    241 	struct place pl;
    242 
    243 	lat = angle(dec);
    244 	lon = angle(ra);
    245 	pl.nlat.l = lat;
    246 	pl.nlat.s = sin(lat);
    247 	pl.nlat.c = cos(lat);
    248 	pl.wlon.l = lon;
    249 	pl.wlon.s = sin(lon);
    250 	pl.wlon.c = cos(lon);
    251 	normalize(&pl);
    252 	return projection(&pl, x, y);
    253 }
    254 
    255 /* end of 'heavens' section */
    256 
    257 int
    258 setmap(int32 ramin, int32 ramax, int32 decmin, int32 decmax, Rectangle r, int zenithup)
    259 {
    260 	int c;
    261 	double minx, maxx, miny, maxy;
    262 
    263 	c = 1000*60*60;
    264 	mapra = ramax/2+ramin/2;
    265 	mapdec = decmax/2+decmin/2;
    266 
    267 	if(zenithup)
    268 		projection = heavens(mylat, mysid, (double)mapdec/c, (double)mapra/c);
    269 	else{
    270 		orient((double)mapdec/c, (double)mapra/c, 0.);
    271 		projection = stereographic();
    272 	}
    273 	mapx0 = (r.max.x+r.min.x)/2;
    274 	mapy0 = (r.max.y+r.min.y)/2;
    275 	maps = ((double)Dy(r))/(double)(decmax-decmin);
    276 	if(maptoxy(ramin, decmin, &minx, &miny) < 0)
    277 		return -1;
    278 	if(maptoxy(ramax, decmax, &maxx, &maxy) < 0)
    279 		return -1;
    280 	/*
    281 	 * It's tricky to get the scale right.  This noble attempt scales
    282 	 * to fit the lengths of the sides of the rectangle.
    283 	 */
    284 	mapscale = Dx(r)/(minx-maxx);
    285 	if(mapscale > Dy(r)/(maxy-miny))
    286 		mapscale = Dy(r)/(maxy-miny);
    287 	/*
    288 	 * near the pole It's not a rectangle, though, so this scales
    289 	 * to fit the corners of the trapezoid (a triangle at the pole).
    290 	 */
    291 	mapscale *= (cos(angle(mapdec))+1.)/2;
    292 	if(maxy  < miny){
    293 		/* upside down, between zenith and pole */
    294 		fprint(2, "reverse plot\n");
    295 		mapscale = -mapscale;
    296 	}
    297 	return 1;
    298 }
    299 
    300 Point
    301 map(int32 ra, int32 dec)
    302 {
    303 	double x, y;
    304 	Point p;
    305 
    306 	if(maptoxy(ra, dec, &x, &y) > 0){
    307 		p.x = mapscale*x + mapx0;
    308 		p.y = mapscale*-y + mapy0;
    309 	}else{
    310 		p.x = -100;
    311 		p.y = -100;
    312 	}
    313 	return p;
    314 }
    315 
    316 int
    317 dsize(int mag)	/* mag is 10*magnitude; return disc size */
    318 {
    319 	double d;
    320 
    321 	mag += 25;	/* make mags all positive; sirius is -1.6m */
    322 	d = (130-mag)/10;
    323 	/* if plate scale is huge, adjust */
    324 	if(maps < 100.0/MILLIARCSEC)
    325 		d *= .71;
    326 	if(maps < 50.0/MILLIARCSEC)
    327 		d *= .71;
    328 	return d;
    329 }
    330 
    331 void
    332 drawname(Image *scr, Image *col, char *s, int ra, int dec)
    333 {
    334 	Point p;
    335 
    336 	if(font == nil)
    337 		return;
    338 	p = addpt(map(ra, dec), Pt(4, -1));	/* font has huge ascent */
    339 	string(scr, p, col, ZP, font, s);
    340 }
    341 
    342 int
    343 npixels(DAngle diam)
    344 {
    345 	Point p0, p1;
    346 
    347 	diam = DEG(angle(diam)*MILLIARCSEC);	/* diam in milliarcsec */
    348 	diam /= 2;	/* radius */
    349 	/* convert to plate scale */
    350 	/* BUG: need +100 because we sometimes crash in library if we plot the exact center */
    351 	p0 = map(mapra+100, mapdec);
    352 	p1 = map(mapra+100+diam, mapdec);
    353 	return hypot(p0.x-p1.x, p0.y-p1.y);
    354 }
    355 
    356 void
    357 drawdisc(Image *scr, DAngle semidiam, int ring, Image *color, Point pt, char *s)
    358 {
    359 	int d;
    360 
    361 	d = npixels(semidiam*2);
    362 	if(d < 5)
    363 		d = 5;
    364 	fillellipse(scr, pt, d, d, color, ZP);
    365 	if(ring == 1)
    366 		ellipse(scr, pt, 5*d/2, d/2, 0, color, ZP);
    367 	if(ring == 2)
    368 		ellipse(scr, pt, d, d, 0, grey, ZP);
    369 	if(s){
    370 		d = stringwidth(font, s);
    371 		pt.x -= d/2;
    372 		pt.y -= font->height/2 + 1;
    373 		string(scr, pt, display->black, ZP, font, s);
    374 	}
    375 }
    376 
    377 void
    378 drawplanet(Image *scr, Planetrec *p, Point pt)
    379 {
    380 	if(strcmp(p->name, "sun") == 0){
    381 		drawdisc(scr, p->semidiam, 0, suncolor, pt, nil);
    382 		return;
    383 	}
    384 	if(strcmp(p->name, "moon") == 0){
    385 		drawdisc(scr, p->semidiam, 0, mooncolor, pt, nil);
    386 		return;
    387 	}
    388 	if(strcmp(p->name, "shadow") == 0){
    389 		drawdisc(scr, p->semidiam, 2, shadowcolor, pt, nil);
    390 		return;
    391 	}
    392 	if(strcmp(p->name, "mercury") == 0){
    393 		drawdisc(scr, p->semidiam, 0, mercurycolor, pt, "m");
    394 		return;
    395 	}
    396 	if(strcmp(p->name, "venus") == 0){
    397 		drawdisc(scr, p->semidiam, 0, venuscolor, pt, "v");
    398 		return;
    399 	}
    400 	if(strcmp(p->name, "mars") == 0){
    401 		drawdisc(scr, p->semidiam, 0, marscolor, pt, "M");
    402 		return;
    403 	}
    404 	if(strcmp(p->name, "jupiter") == 0){
    405 		drawdisc(scr, p->semidiam, 0, jupitercolor, pt, "J");
    406 		return;
    407 	}
    408 	if(strcmp(p->name, "saturn") == 0){
    409 		drawdisc(scr, p->semidiam, 1, saturncolor, pt, "S");
    410 
    411 		return;
    412 	}
    413 	if(strcmp(p->name, "uranus") == 0){
    414 		drawdisc(scr, p->semidiam, 0, uranuscolor, pt, "U");
    415 
    416 		return;
    417 	}
    418 	if(strcmp(p->name, "neptune") == 0){
    419 		drawdisc(scr, p->semidiam, 0, neptunecolor, pt, "N");
    420 
    421 		return;
    422 	}
    423 	if(strcmp(p->name, "pluto") == 0){
    424 		drawdisc(scr, p->semidiam, 0, plutocolor, pt, "P");
    425 
    426 		return;
    427 	}
    428 	if(strcmp(p->name, "comet") == 0){
    429 		drawdisc(scr, p->semidiam, 0, cometcolor, pt, "C");
    430 		return;
    431 	}
    432 
    433 	pt.x -= stringwidth(font, p->name)/2;
    434 	pt.y -= font->height/2;
    435 	string(scr, pt, grey, ZP, font, p->name);
    436 }
    437 
    438 void
    439 tolast(char *name)
    440 {
    441 	int i, nlast;
    442 	Record *r, rr;
    443 
    444 	/* stop early to simplify inner loop adjustment */
    445 	nlast = 0;
    446 	for(i=0,r=rec; i<nrec-nlast; i++,r++)
    447 		if(r->type == Planet)
    448 			if(name==nil || strcmp(r->u.planet.name, name)==0){
    449 				rr = *r;
    450 				memmove(rec+i, rec+i+1, (nrec-i-1)*sizeof(Record));
    451 				rec[nrec-1] = rr;
    452 				--i;
    453 				--r;
    454 				nlast++;
    455 			}
    456 }
    457 
    458 int
    459 bbox(int32 extrara, int32 extradec, int quantize)
    460 {
    461 	int i;
    462 	Record *r;
    463 	int ra, dec;
    464 	int rah, ram, d1, d2;
    465 	double r0;
    466 
    467 	ramin = 0x7FFFFFFF;
    468 	ramax = -0x7FFFFFFF;
    469 	decmin = 0x7FFFFFFF;
    470 	decmax = -0x7FFFFFFF;
    471 
    472 	for(i=0,r=rec; i<nrec; i++,r++){
    473 		if(r->type == Patch){
    474 			radec(r->index, &rah, &ram, &dec);
    475 			ra = 15*rah+ram/4;
    476 			r0 = c/cos(dec*PI/180);
    477 			ra *= c;
    478 			dec *= c;
    479 			if(dec == 0)
    480 				d1 = c, d2 = c;
    481 			else if(dec < 0)
    482 				d1 = c, d2 = 0;
    483 			else
    484 				d1 = 0, d2 = c;
    485 		}else if(r->type==SAO || r->type==NGC || r->type==Planet || r->type==Abell){
    486 			ra = r->u.ngc.ra;
    487 			dec = r->u.ngc.dec;
    488 			d1 = 0, d2 = 0, r0 = 0;
    489 		}else
    490 			continue;
    491 		if(dec+d2+extradec > decmax)
    492 			decmax = dec+d2+extradec;
    493 		if(dec-d1-extradec < decmin)
    494 			decmin = dec-d1-extradec;
    495 		if(folded){
    496 			ra -= 180*c;
    497 			if(ra < 0)
    498 				ra += 360*c;
    499 		}
    500 		if(ra+r0+extrara > ramax)
    501 			ramax = ra+r0+extrara;
    502 		if(ra-extrara < ramin)
    503 			ramin = ra-extrara;
    504 	}
    505 
    506 	if(decmax > 90*c)
    507 		decmax = 90*c;
    508 	if(decmin < -90*c)
    509 		decmin = -90*c;
    510 	if(ramin < 0)
    511 		ramin += 360*c;
    512 	if(ramax >= 360*c)
    513 		ramax -= 360*c;
    514 
    515 	if(quantize){
    516 		/* quantize to degree boundaries */
    517 		ramin -= ramin%m5;
    518 		if(ramax%m5 != 0)
    519 			ramax += m5-(ramax%m5);
    520 		if(decmin > 0)
    521 			decmin -= decmin%c;
    522 		else
    523 			decmin -= c - (-decmin)%c;
    524 		if(decmax > 0){
    525 			if(decmax%c != 0)
    526 				decmax += c-(decmax%c);
    527 		}else if(decmax < 0){
    528 			if(decmax%c != 0)
    529 				decmax += ((-decmax)%c);
    530 		}
    531 	}
    532 
    533 	if(folded){
    534 		if(ramax-ramin > 270*c){
    535 			fprint(2, "ra range too wide %ld°\n", (ramax-ramin)/c);
    536 			return -1;
    537 		}
    538 	}else if(ramax-ramin > 270*c){
    539 		folded = 1;
    540 		return bbox(0, 0, quantize);
    541 	}
    542 
    543 	return 1;
    544 }
    545 
    546 int
    547 inbbox(DAngle ra, DAngle dec)
    548 {
    549 	int min;
    550 
    551 	if(dec < decmin)
    552 		return 0;
    553 	if(dec > decmax)
    554 		return 0;
    555 	min = ramin;
    556 	if(ramin > ramax){	/* straddling 0h0m */
    557 		min -= 360*c;
    558 		if(ra > 180*c)
    559 			ra -= 360*c;
    560 	}
    561 	if(ra < min)
    562 		return 0;
    563 	if(ra > ramax)
    564 		return 0;
    565 	return 1;
    566 }
    567 
    568 int
    569 gridra(int32 mapdec)
    570 {
    571 	mapdec = abs(mapdec)+c/2;
    572 	mapdec /= c;
    573 	if(mapdec < 60)
    574 		return m5;
    575 	if(mapdec < 80)
    576 		return 2*m5;
    577 	if(mapdec < 85)
    578 		return 4*m5;
    579 	return 8*m5;
    580 }
    581 
    582 #define	GREY	(nogrey? display->white : grey)
    583 
    584 void
    585 plot(char *flags)
    586 {
    587 	int i, j, k;
    588 	char *t;
    589 	int32 x, y;
    590 	int ra, dec;
    591 	int m;
    592 	Point p, pts[10];
    593 	Record *r;
    594 	Rectangle rect, r1;
    595 	int dx, dy, nogrid, textlevel, nogrey, zenithup;
    596 	Image *scr;
    597 	char *name, buf[32];
    598 	double v;
    599 
    600 	if(plotopen() < 0)
    601 		return;
    602 	nogrid = 0;
    603 	nogrey = 0;
    604 	textlevel = 1;
    605 	dx = 512;
    606 	dy = 512;
    607 	zenithup = 0;
    608 	for(;;){
    609 		if(t = alpha(flags, "nogrid")){
    610 			nogrid = 1;
    611 			flags = t;
    612 			continue;
    613 		}
    614 		if((t = alpha(flags, "zenith")) || (t = alpha(flags, "zenithup")) ){
    615 			zenithup = 1;
    616 			flags = t;
    617 			continue;
    618 		}
    619 		if((t = alpha(flags, "notext")) || (t = alpha(flags, "nolabel")) ){
    620 			textlevel = 0;
    621 			flags = t;
    622 			continue;
    623 		}
    624 		if((t = alpha(flags, "alltext")) || (t = alpha(flags, "alllabel")) ){
    625 			textlevel = 2;
    626 			flags = t;
    627 			continue;
    628 		}
    629 		if(t = alpha(flags, "dx")){
    630 			dx = strtol(t, &t, 0);
    631 			if(dx < 100){
    632 				fprint(2, "dx %d too small (min 100) in plot\n", dx);
    633 				return;
    634 			}
    635 			flags = skipbl(t);
    636 			continue;
    637 		}
    638 		if(t = alpha(flags, "dy")){
    639 			dy = strtol(t, &t, 0);
    640 			if(dy < 100){
    641 				fprint(2, "dy %d too small (min 100) in plot\n", dy);
    642 				return;
    643 			}
    644 			flags = skipbl(t);
    645 			continue;
    646 		}
    647 		if((t = alpha(flags, "nogrey")) || (t = alpha(flags, "nogray"))){
    648 			nogrey = 1;
    649 			flags = skipbl(t);
    650 			continue;
    651 		}
    652 		if(*flags){
    653 			fprint(2, "syntax error in plot\n");
    654 			return;
    655 		}
    656 		break;
    657 	}
    658 	flatten();
    659 	folded = 0;
    660 
    661 	if(bbox(0, 0, 1) < 0)
    662 		return;
    663 	if(ramax-ramin<100 || decmax-decmin<100){
    664 		fprint(2, "plot too small\n");
    665 		return;
    666 	}
    667 
    668 	scr = allocimage(display, Rect(0, 0, dx, dy), RGB24, 0, DBlack);
    669 	if(scr == nil){
    670 		fprint(2, "can't allocate image: %r\n");
    671 		return;
    672 	}
    673 	rect = scr->r;
    674 	rect.min.x += 16;
    675 	rect = insetrect(rect, 40);
    676 	if(setmap(ramin, ramax, decmin, decmax, rect, zenithup) < 0){
    677 		fprint(2, "can't set up map coordinates\n");
    678 		return;
    679 	}
    680 	if(!nogrid){
    681 		for(x=ramin; ; ){
    682 			for(j=0; j<nelem(pts); j++){
    683 				/* use double to avoid overflow */
    684 				v = (double)j / (double)(nelem(pts)-1);
    685 				v = decmin + v*(decmax-decmin);
    686 				pts[j] = map(x, v);
    687 			}
    688 			bezspline(scr, pts, nelem(pts), Endsquare, Endsquare, 0, GREY, ZP);
    689 			ra = x;
    690 			if(folded){
    691 				ra -= 180*c;
    692 				if(ra < 0)
    693 					ra += 360*c;
    694 			}
    695 			p = pts[0];
    696 			p.x -= stringwidth(font, hm5(angle(ra)))/2;
    697 			string(scr, p, GREY, ZP, font, hm5(angle(ra)));
    698 			p = pts[nelem(pts)-1];
    699 			p.x -= stringwidth(font, hm5(angle(ra)))/2;
    700 			p.y -= font->height;
    701 			string(scr, p, GREY, ZP, font, hm5(angle(ra)));
    702 			if(x == ramax)
    703 				break;
    704 			x += gridra(mapdec);
    705 			if(x > ramax)
    706 				x = ramax;
    707 		}
    708 		for(y=decmin; y<=decmax; y+=c){
    709 			for(j=0; j<nelem(pts); j++){
    710 				/* use double to avoid overflow */
    711 				v = (double)j / (double)(nelem(pts)-1);
    712 				v = ramin + v*(ramax-ramin);
    713 				pts[j] = map(v, y);
    714 			}
    715 			bezspline(scr, pts, nelem(pts), Endsquare, Endsquare, 0, GREY, ZP);
    716 			p = pts[0];
    717 			p.x += 3;
    718 			p.y -= font->height/2;
    719 			string(scr, p, GREY, ZP, font, deg(angle(y)));
    720 			p = pts[nelem(pts)-1];
    721 			p.x -= 3+stringwidth(font, deg(angle(y)));
    722 			p.y -= font->height/2;
    723 			string(scr, p, GREY, ZP, font, deg(angle(y)));
    724 		}
    725 	}
    726 	/* reorder to get planets in front of stars */
    727 	tolast(nil);
    728 	tolast("moon");		/* moon is in front of everything... */
    729 	tolast("shadow");	/* ... except the shadow */
    730 
    731 	for(i=0,r=rec; i<nrec; i++,r++){
    732 		dec = r->u.ngc.dec;
    733 		ra = r->u.ngc.ra;
    734 		if(folded){
    735 			ra -= 180*c;
    736 			if(ra < 0)
    737 				ra += 360*c;
    738 		}
    739 		if(textlevel){
    740 			name = nameof(r);
    741 			if(name==nil && textlevel>1 && r->type==SAO){
    742 				snprint(buf, sizeof buf, "SAO%ld", r->index);
    743 				name = buf;
    744 			}
    745 			if(name)
    746 				drawname(scr, nogrey? display->white : alphagrey, name, ra, dec);
    747 		}
    748 		if(r->type == Planet){
    749 			drawplanet(scr, &r->u.planet, map(ra, dec));
    750 			continue;
    751 		}
    752 		if(r->type == SAO){
    753 			m = r->u.sao.mag;
    754 			if(m == UNKNOWNMAG)
    755 				m = r->u.sao.mpg;
    756 			if(m == UNKNOWNMAG)
    757 				continue;
    758 			m = dsize(m);
    759 			if(m < 3)
    760 				fillellipse(scr, map(ra, dec), m, m, nogrey? display->white : lightgrey, ZP);
    761 			else{
    762 				ellipse(scr, map(ra, dec), m+1, m+1, 0, display->black, ZP);
    763 				fillellipse(scr, map(ra, dec), m, m, display->white, ZP);
    764 			}
    765 			continue;
    766 		}
    767 		if(r->type == Abell){
    768 			ellipse(scr, addpt(map(ra, dec), Pt(-3, 2)), 2, 1, 0, lightblue, ZP);
    769 			ellipse(scr, addpt(map(ra, dec), Pt(3, 2)), 2, 1, 0, lightblue, ZP);
    770 			ellipse(scr, addpt(map(ra, dec), Pt(0, -2)), 1, 2, 0, lightblue, ZP);
    771 			continue;
    772 		}
    773 		switch(r->u.ngc.type){
    774 		case Galaxy:
    775 			j = npixels(r->u.ngc.diam);
    776 			if(j < 4)
    777 				j = 4;
    778 			if(j > 10)
    779 				k = j/3;
    780 			else
    781 				k = j/2;
    782 			ellipse(scr, map(ra, dec), j, k, 0, lightblue, ZP);
    783 			break;
    784 
    785 		case PlanetaryN:
    786 			p = map(ra, dec);
    787 			j = npixels(r->u.ngc.diam);
    788 			if(j < 3)
    789 				j = 3;
    790 			ellipse(scr, p, j, j, 0, green, ZP);
    791 			line(scr, Pt(p.x, p.y+j+1), Pt(p.x, p.y+j+4),
    792 				Endsquare, Endsquare, 0, green, ZP);
    793 			line(scr, Pt(p.x, p.y-(j+1)), Pt(p.x, p.y-(j+4)),
    794 				Endsquare, Endsquare, 0, green, ZP);
    795 			line(scr, Pt(p.x+j+1, p.y), Pt(p.x+j+4, p.y),
    796 				Endsquare, Endsquare, 0, green, ZP);
    797 			line(scr, Pt(p.x-(j+1), p.y), Pt(p.x-(j+4), p.y),
    798 				Endsquare, Endsquare, 0, green, ZP);
    799 			break;
    800 
    801 		case DiffuseN:
    802 		case NebularCl:
    803 			p = map(ra, dec);
    804 			j = npixels(r->u.ngc.diam);
    805 			if(j < 4)
    806 				j = 4;
    807 			r1.min = Pt(p.x-j, p.y-j);
    808 			r1.max = Pt(p.x+j, p.y+j);
    809 			if(r->u.ngc.type != DiffuseN)
    810 				draw(scr, r1, ocstipple, ocstipple, ZP);
    811 			line(scr, Pt(p.x-j, p.y-j), Pt(p.x+j, p.y-j),
    812 				Endsquare, Endsquare, 0, green, ZP);
    813 			line(scr, Pt(p.x-j, p.y+j), Pt(p.x+j, p.y+j),
    814 				Endsquare, Endsquare, 0, green, ZP);
    815 			line(scr, Pt(p.x-j, p.y-j), Pt(p.x-j, p.y+j),
    816 				Endsquare, Endsquare, 0, green, ZP);
    817 			line(scr, Pt(p.x+j, p.y-j), Pt(p.x+j, p.y+j),
    818 				Endsquare, Endsquare, 0, green, ZP);
    819 			break;
    820 
    821 		case OpenCl:
    822 			p = map(ra, dec);
    823 			j = npixels(r->u.ngc.diam);
    824 			if(j < 4)
    825 				j = 4;
    826 			fillellipse(scr, p, j, j, ocstipple, ZP);
    827 			break;
    828 
    829 		case GlobularCl:
    830 			j = npixels(r->u.ngc.diam);
    831 			if(j < 4)
    832 				j = 4;
    833 			p = map(ra, dec);
    834 			ellipse(scr, p, j, j, 0, lightgrey, ZP);
    835 			line(scr, Pt(p.x-(j-1), p.y), Pt(p.x+j, p.y),
    836 				Endsquare, Endsquare, 0, lightgrey, ZP);
    837 			line(scr, Pt(p.x, p.y-(j-1)), Pt(p.x, p.y+j),
    838 				Endsquare, Endsquare, 0, lightgrey, ZP);
    839 			break;
    840 
    841 		}
    842 	}
    843 	flushimage(display, 1);
    844 	displayimage(scr);
    845 }
    846 
    847 int
    848 runcommand(char *command, int p[2])
    849 {
    850 	switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
    851 	case -1:
    852 		return -1;
    853 	default:
    854 		break;
    855 	case 0:
    856 		dup(p[1], 1);
    857 		close(p[0]);
    858 		close(p[1]);
    859 		execlp("rc", "rc", "-c", command, nil);
    860 		fprint(2, "can't exec %s: %r", command);
    861 		exits(nil);
    862 	}
    863 	return 1;
    864 }
    865 
    866 void
    867 parseplanet(char *line, Planetrec *p)
    868 {
    869 	char *fld[6];
    870 	int i, nfld;
    871 	char *s;
    872 
    873 	if(line[0] == '\0')
    874 		return;
    875 	line[10] = '\0';	/* terminate name */
    876 	s = strrchr(line, ' ');
    877 	if(s == nil)
    878 		s = line;
    879 	else
    880 		s++;
    881 	strcpy(p->name, s);
    882 	for(i=0; s[i]!='\0'; i++)
    883 		p->name[i] = tolower(s[i]);
    884 	p->name[i] = '\0';
    885 	nfld = getfields(line+11, fld, nelem(fld), 1, " ");
    886 	p->ra = dangle(getra(fld[0]));
    887 	p->dec = dangle(getra(fld[1]));
    888 	p->az = atof(fld[2])*MILLIARCSEC;
    889 	p->alt = atof(fld[3])*MILLIARCSEC;
    890 	p->semidiam = atof(fld[4])*1000;
    891 	if(nfld > 5)
    892 		p->phase = atof(fld[5]);
    893 	else
    894 		p->phase = 0;
    895 }
    896 
    897 void
    898 astro(char *flags, int initial)
    899 {
    900 	int p[2];
    901 	int i, n, np;
    902 	char cmd[256], buf[4096], *lines[20], *fld[10];
    903 
    904 	snprint(cmd, sizeof cmd, "astro -p %s", flags);
    905 	if(pipe(p) < 0){
    906 		fprint(2, "can't pipe: %r\n");
    907 		return;
    908 	}
    909 	if(runcommand(cmd, p) < 0){
    910 		close(p[0]);
    911 		close(p[1]);
    912 		fprint(2, "can't run astro: %r");
    913 		return;
    914 	}
    915 	close(p[1]);
    916 	n = readn(p[0], buf, sizeof buf-1);
    917 	if(n <= 0){
    918 		fprint(2, "no data from astro\n");
    919 		return;
    920 	}
    921 	if(!initial)
    922 		Bwrite(&bout, buf, n);
    923 	buf[n] = '\0';
    924 	np = getfields(buf, lines, nelem(lines), 0, "\n");
    925 	if(np <= 1){
    926 		fprint(2, "astro: not enough output\n");
    927 		return;
    928 	}
    929 	Bprint(&bout, "%s\n", lines[0]);
    930 	Bflush(&bout);
    931 	/* get latitude and longitude */
    932 	if(getfields(lines[0], fld, nelem(fld), 1, " ") < 8)
    933 		fprint(2, "astro: can't read longitude: too few fields\n");
    934 	else{
    935 		mysid = getra(fld[5])*180./PI;
    936 		mylat = getra(fld[6])*180./PI;
    937 		mylon = getra(fld[7])*180./PI;
    938 	}
    939 	/*
    940 	 * Each time we run astro, we generate a new planet list
    941 	 * so multiple appearances of a planet may exist as we plot
    942 	 * its motion over time.
    943 	 */
    944 	planet = malloc(NPlanet*sizeof planet[0]);
    945 	if(planet == nil){
    946 		fprint(2, "astro: malloc failed: %r\n");
    947 		exits("malloc");
    948 	}
    949 	memset(planet, 0, NPlanet*sizeof planet[0]);
    950 	for(i=1; i<np; i++)
    951 		parseplanet(lines[i], &planet[i-1]);
    952 }