URI: 
       tplot.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tplot.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 }