Blob


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"
12 /* convert to milliarcsec */
13 static int32 c = MILLIARCSEC; /* 1 degree */
14 static int32 m5 = 1250*60*60; /* 5 minutes of ra */
16 DAngle ramin;
17 DAngle ramax;
18 DAngle decmin;
19 DAngle decmax;
20 int folded;
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;
41 Planetrec *planet;
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*);
50 char *fontname = "/lib/font/bit/luc/unicode.6.font";
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 */
58 typedef struct Xyz Xyz;
59 typedef struct coord Coord;
60 typedef struct Loc Loc;
62 struct Xyz{
63 double x,y,z;
64 };
66 struct Loc{
67 Coord nlat, elon;
68 };
70 Xyz north = { 0, 0, 1 };
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);
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;
108 /*
109 * Function heavens() for setting up observer-eye-view
110 * sky charts, plus subsidiary functions.
111 * Written by Doug McIlroy.
112 */
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 */
123 Coord
124 mkcoord(double degrees)
126 Coord c;
128 c.l = degrees*PI/180;
129 c.s = sin(c.l);
130 c.c = cos(c.l);
131 return c;
134 Xyz
135 ptov(struct place p)
137 Xyz v;
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;
145 double
146 dot(Xyz a, Xyz b)
148 return a.x*b.x + a.y*b.y + a.z*b.z;
151 Xyz
152 cross(Xyz a, Xyz b)
154 Xyz v;
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;
162 double
163 len(Xyz a)
165 return sqrt(dot(a, a));
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. */
177 Xyz
178 azvec(Xyz a, Xyz b)
180 return cross(cross(a,b), a);
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 */
187 double
188 azimuth(Xyz a, Xyz b)
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));
196 if(dot(b,cross(north,a)) < 0)
197 az = -az;
198 return az;
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 */
207 double
208 rot(struct place center, struct place zenith)
210 Xyz cen = ptov(center);
211 Xyz zen = ptov(zenith);
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);
222 int (*
223 heavens(double zlatdeg, double zlondeg, double clatdeg, double clondeg))(struct place*, double*, double*)
225 struct place center;
226 struct place zenith;
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;
237 int
238 maptoxy(int32 ra, int32 dec, double *x, double *y)
240 double lat, lon;
241 struct place pl;
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);
255 /* end of 'heavens' section */
257 int
258 setmap(int32 ramin, int32 ramax, int32 decmin, int32 decmax, Rectangle r, int zenithup)
260 int c;
261 double minx, maxx, miny, maxy;
263 c = 1000*60*60;
264 mapra = ramax/2+ramin/2;
265 mapdec = decmax/2+decmin/2;
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();
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;
297 return 1;
300 Point
301 map(int32 ra, int32 dec)
303 double x, y;
304 Point p;
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;
313 return p;
316 int
317 dsize(int mag) /* mag is 10*magnitude; return disc size */
319 double d;
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;
331 void
332 drawname(Image *scr, Image *col, char *s, int ra, int dec)
334 Point p;
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);
342 int
343 npixels(DAngle diam)
345 Point p0, p1;
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);
356 void
357 drawdisc(Image *scr, DAngle semidiam, int ring, Image *color, Point pt, char *s)
359 int d;
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);
377 void
378 drawplanet(Image *scr, Planetrec *p, Point pt)
380 if(strcmp(p->name, "sun") == 0){
381 drawdisc(scr, p->semidiam, 0, suncolor, pt, nil);
382 return;
384 if(strcmp(p->name, "moon") == 0){
385 drawdisc(scr, p->semidiam, 0, mooncolor, pt, nil);
386 return;
388 if(strcmp(p->name, "shadow") == 0){
389 drawdisc(scr, p->semidiam, 2, shadowcolor, pt, nil);
390 return;
392 if(strcmp(p->name, "mercury") == 0){
393 drawdisc(scr, p->semidiam, 0, mercurycolor, pt, "m");
394 return;
396 if(strcmp(p->name, "venus") == 0){
397 drawdisc(scr, p->semidiam, 0, venuscolor, pt, "v");
398 return;
400 if(strcmp(p->name, "mars") == 0){
401 drawdisc(scr, p->semidiam, 0, marscolor, pt, "M");
402 return;
404 if(strcmp(p->name, "jupiter") == 0){
405 drawdisc(scr, p->semidiam, 0, jupitercolor, pt, "J");
406 return;
408 if(strcmp(p->name, "saturn") == 0){
409 drawdisc(scr, p->semidiam, 1, saturncolor, pt, "S");
411 return;
413 if(strcmp(p->name, "uranus") == 0){
414 drawdisc(scr, p->semidiam, 0, uranuscolor, pt, "U");
416 return;
418 if(strcmp(p->name, "neptune") == 0){
419 drawdisc(scr, p->semidiam, 0, neptunecolor, pt, "N");
421 return;
423 if(strcmp(p->name, "pluto") == 0){
424 drawdisc(scr, p->semidiam, 0, plutocolor, pt, "P");
426 return;
428 if(strcmp(p->name, "comet") == 0){
429 drawdisc(scr, p->semidiam, 0, cometcolor, pt, "C");
430 return;
433 pt.x -= stringwidth(font, p->name)/2;
434 pt.y -= font->height/2;
435 string(scr, pt, grey, ZP, font, p->name);
438 void
439 tolast(char *name)
441 int i, nlast;
442 Record *r, rr;
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++;
458 int
459 bbox(int32 extrara, int32 extradec, int quantize)
461 int i;
462 Record *r;
463 int ra, dec;
464 int rah, ram, d1, d2;
465 double r0;
467 ramin = 0x7FFFFFFF;
468 ramax = -0x7FFFFFFF;
469 decmin = 0x7FFFFFFF;
470 decmax = -0x7FFFFFFF;
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;
500 if(ra+r0+extrara > ramax)
501 ramax = ra+r0+extrara;
502 if(ra-extrara < ramin)
503 ramin = ra-extrara;
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;
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);
533 if(folded){
534 if(ramax-ramin > 270*c){
535 fprint(2, "ra range too wide %ldĀ°\n", (ramax-ramin)/c);
536 return -1;
538 }else if(ramax-ramin > 270*c){
539 folded = 1;
540 return bbox(0, 0, quantize);
543 return 1;
546 int
547 inbbox(DAngle ra, DAngle dec)
549 int min;
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;
561 if(ra < min)
562 return 0;
563 if(ra > ramax)
564 return 0;
565 return 1;
568 int
569 gridra(int32 mapdec)
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;
582 #define GREY (nogrey? display->white : grey)
584 void
585 plot(char *flags)
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;
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;
614 if((t = alpha(flags, "zenith")) || (t = alpha(flags, "zenithup")) ){
615 zenithup = 1;
616 flags = t;
617 continue;
619 if((t = alpha(flags, "notext")) || (t = alpha(flags, "nolabel")) ){
620 textlevel = 0;
621 flags = t;
622 continue;
624 if((t = alpha(flags, "alltext")) || (t = alpha(flags, "alllabel")) ){
625 textlevel = 2;
626 flags = t;
627 continue;
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;
635 flags = skipbl(t);
636 continue;
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;
644 flags = skipbl(t);
645 continue;
647 if((t = alpha(flags, "nogrey")) || (t = alpha(flags, "nogray"))){
648 nogrey = 1;
649 flags = skipbl(t);
650 continue;
652 if(*flags){
653 fprint(2, "syntax error in plot\n");
654 return;
656 break;
658 flatten();
659 folded = 0;
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;
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;
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;
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);
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;
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;
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);
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)));
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 */
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;
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;
745 if(name)
746 drawname(scr, nogrey? display->white : alphagrey, name, ra, dec);
748 if(r->type == Planet){
749 drawplanet(scr, &r->u.planet, map(ra, dec));
750 continue;
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);
765 continue;
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;
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;
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;
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;
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;
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;
843 flushimage(display, 1);
844 displayimage(scr);
847 int
848 runcommand(char *command, int p[2])
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);
863 return 1;
866 void
867 parseplanet(char *line, Planetrec *p)
869 char *fld[6];
870 int i, nfld;
871 char *s;
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;
897 void
898 astro(char *flags, int initial)
900 int p[2];
901 int i, n, np;
902 char cmd[256], buf[4096], *lines[20], *fld[10];
904 snprint(cmd, sizeof cmd, "astro -p %s", flags);
905 if(pipe(p) < 0){
906 fprint(2, "can't pipe: %r\n");
907 return;
909 if(runcommand(cmd, p) < 0){
910 close(p[0]);
911 close(p[1]);
912 fprint(2, "can't run astro: %r");
913 return;
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;
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;
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;
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");
949 memset(planet, 0, NPlanet*sizeof planet[0]);
950 for(i=1; i<np; i++)
951 parseplanet(lines[i], &planet[i-1]);