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