blob: ade4ac5b36b8d78792c304e8c833533fb10ddb6a [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include <ctype.h>
#include "map.h"
#undef RAD
#undef TWOPI
#include "sky.h"
/* convert to milliarcsec */
static int32 c = MILLIARCSEC; /* 1 degree */
static int32 m5 = 1250*60*60; /* 5 minutes of ra */
DAngle ramin;
DAngle ramax;
DAngle decmin;
DAngle decmax;
int folded;
Image *grey;
Image *alphagrey;
Image *green;
Image *lightblue;
Image *lightgrey;
Image *ocstipple;
Image *suncolor;
Image *mooncolor;
Image *shadowcolor;
Image *mercurycolor;
Image *venuscolor;
Image *marscolor;
Image *jupitercolor;
Image *saturncolor;
Image *uranuscolor;
Image *neptunecolor;
Image *plutocolor;
Image *cometcolor;
Planetrec *planet;
int32 mapx0, mapy0;
int32 mapra, mapdec;
double mylat, mylon, mysid;
double mapscale;
double maps;
int (*projection)(struct place*, double*, double*);
char *fontname = "/lib/font/bit/luc/unicode.6.font";
/* types Coord and Loc correspond to types in map(3) thus:
Coord == struct coord;
Loc == struct place, except longitudes are measured
positive east for Loc and west for struct place
*/
typedef struct Xyz Xyz;
typedef struct coord Coord;
typedef struct Loc Loc;
struct Xyz{
double x,y,z;
};
struct Loc{
Coord nlat, elon;
};
Xyz north = { 0, 0, 1 };
int
plotopen(void)
{
if(display != nil)
return 1;
if(initdraw(drawerror, nil, nil) < 0){
fprint(2, "initdisplay failed: %r\n");
return -1;
}
grey = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x777777FF);
lightgrey = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0xAAAAAAFF);
alphagrey = allocimage(display, Rect(0, 0, 2, 2), RGBA32, 1, 0x77777777);
green = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x00AA00FF);
lightblue = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, 0x009EEEFF);
ocstipple = allocimage(display, Rect(0, 0, 2, 2), CMAP8, 1, 0xAAAAAAFF);
draw(ocstipple, Rect(0, 0, 1, 1), display->black, nil, ZP);
draw(ocstipple, Rect(1, 1, 2, 2), display->black, nil, ZP);
suncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFFF77FF);
mooncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAAAFF);
shadowcolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x00000055);
mercurycolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFAAAAFF);
venuscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAFFFF);
marscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFF5555FF);
jupitercolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xFFFFAAFF);
saturncolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAFFAAFF);
uranuscolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x77DDDDFF);
neptunecolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x77FF77FF);
plutocolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0x7777FFFF);
cometcolor = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, 0xAAAAFFFF);
font = openfont(display, fontname);
if(font == nil)
fprint(2, "warning: no font %s: %r\n", fontname);
return 1;
}
/*
* Function heavens() for setting up observer-eye-view
* sky charts, plus subsidiary functions.
* Written by Doug McIlroy.
*/
/* heavens(nlato, wlono, nlatc, wlonc) sets up a map(3)-style
coordinate change (by calling orient()) and returns a
projection function heavens for calculating a star map
centered on nlatc,wlonc and rotated so it will appear
upright as seen by a ground observer at nlato,wlono.
all coordinates (north latitude and west longitude)
are in degrees.
*/
Coord
mkcoord(double degrees)
{
Coord c;
c.l = degrees*PI/180;
c.s = sin(c.l);
c.c = cos(c.l);
return c;
}
Xyz
ptov(struct place p)
{
Xyz v;
v.z = p.nlat.s;
v.x = p.nlat.c * p.wlon.c;
v.y = -p.nlat.c * p.wlon.s;
return v;
}
double
dot(Xyz a, Xyz b)
{
return a.x*b.x + a.y*b.y + a.z*b.z;
}
Xyz
cross(Xyz a, Xyz b)
{
Xyz v;
v.x = a.y*b.z - a.z*b.y;
v.y = a.z*b.x - a.x*b.z;
v.z = a.x*b.y - a.y*b.x;
return v;
}
double
len(Xyz a)
{
return sqrt(dot(a, a));
}
/* An azimuth vector from a to b is a tangent to
the sphere at a pointing along the (shorter)
great-circle direction to b. It lies in the
plane of the vectors a and b, is perpendicular
to a, and has a positive compoent along b,
provided a and b span a 2-space. Otherwise
it is null. (aXb)Xa, where X denotes cross
product, is such a vector. */
Xyz
azvec(Xyz a, Xyz b)
{
return cross(cross(a,b), a);
}
/* Find the azimuth of point b as seen
from point a, given that a is distinct
from either pole and from b */
double
azimuth(Xyz a, Xyz b)
{
Xyz aton = azvec(a,north);
Xyz atob = azvec(a,b);
double lenaton = len(aton);
double lenatob = len(atob);
double az = acos(dot(aton,atob)/(lenaton*lenatob));
if(dot(b,cross(north,a)) < 0)
az = -az;
return az;
}
/* Find the rotation (3rd argument of orient() in the
map projection package) for the map described
below. The return is radians; it must be converted
to degrees for orient().
*/
double
rot(struct place center, struct place zenith)
{
Xyz cen = ptov(center);
Xyz zen = ptov(zenith);
if(cen.z > 1-FUZZ) /* center at N pole */
return PI + zenith.wlon.l;
if(cen.z < FUZZ-1) /* at S pole */
return -zenith.wlon.l;
if(fabs(dot(cen,zen)) > 1-FUZZ) /* at zenith */
return 0;
return azimuth(cen, zen);
}
int (*
heavens(double zlatdeg, double zlondeg, double clatdeg, double clondeg))(struct place*, double*, double*)
{
struct place center;
struct place zenith;
center.nlat = mkcoord(clatdeg);
center.wlon = mkcoord(clondeg);
zenith.nlat = mkcoord(zlatdeg);
zenith.wlon = mkcoord(zlondeg);
projection = stereographic();
orient(clatdeg, clondeg, rot(center, zenith)*180/PI);
return projection;
}
int
maptoxy(int32 ra, int32 dec, double *x, double *y)
{
double lat, lon;
struct place pl;
lat = angle(dec);
lon = angle(ra);
pl.nlat.l = lat;
pl.nlat.s = sin(lat);
pl.nlat.c = cos(lat);
pl.wlon.l = lon;
pl.wlon.s = sin(lon);
pl.wlon.c = cos(lon);
normalize(&pl);
return projection(&pl, x, y);
}
/* end of 'heavens' section */
int
setmap(int32 ramin, int32 ramax, int32 decmin, int32 decmax, Rectangle r, int zenithup)
{
int c;
double minx, maxx, miny, maxy;
c = 1000*60*60;
mapra = ramax/2+ramin/2;
mapdec = decmax/2+decmin/2;
if(zenithup)
projection = heavens(mylat, mysid, (double)mapdec/c, (double)mapra/c);
else{
orient((double)mapdec/c, (double)mapra/c, 0.);
projection = stereographic();
}
mapx0 = (r.max.x+r.min.x)/2;
mapy0 = (r.max.y+r.min.y)/2;
maps = ((double)Dy(r))/(double)(decmax-decmin);
if(maptoxy(ramin, decmin, &minx, &miny) < 0)
return -1;
if(maptoxy(ramax, decmax, &maxx, &maxy) < 0)
return -1;
/*
* It's tricky to get the scale right. This noble attempt scales
* to fit the lengths of the sides of the rectangle.
*/
mapscale = Dx(r)/(minx-maxx);
if(mapscale > Dy(r)/(maxy-miny))
mapscale = Dy(r)/(maxy-miny);
/*
* near the pole It's not a rectangle, though, so this scales
* to fit the corners of the trapezoid (a triangle at the pole).
*/
mapscale *= (cos(angle(mapdec))+1.)/2;
if(maxy < miny){
/* upside down, between zenith and pole */
fprint(2, "reverse plot\n");
mapscale = -mapscale;
}
return 1;
}
Point
map(int32 ra, int32 dec)
{
double x, y;
Point p;
if(maptoxy(ra, dec, &x, &y) > 0){
p.x = mapscale*x + mapx0;
p.y = mapscale*-y + mapy0;
}else{
p.x = -100;
p.y = -100;
}
return p;
}
int
dsize(int mag) /* mag is 10*magnitude; return disc size */
{
double d;
mag += 25; /* make mags all positive; sirius is -1.6m */
d = (130-mag)/10;
/* if plate scale is huge, adjust */
if(maps < 100.0/MILLIARCSEC)
d *= .71;
if(maps < 50.0/MILLIARCSEC)
d *= .71;
return d;
}
void
drawname(Image *scr, Image *col, char *s, int ra, int dec)
{
Point p;
if(font == nil)
return;
p = addpt(map(ra, dec), Pt(4, -1)); /* font has huge ascent */
string(scr, p, col, ZP, font, s);
}
int
npixels(DAngle diam)
{
Point p0, p1;
diam = DEG(angle(diam)*MILLIARCSEC); /* diam in milliarcsec */
diam /= 2; /* radius */
/* convert to plate scale */
/* BUG: need +100 because we sometimes crash in library if we plot the exact center */
p0 = map(mapra+100, mapdec);
p1 = map(mapra+100+diam, mapdec);
return hypot(p0.x-p1.x, p0.y-p1.y);
}
void
drawdisc(Image *scr, DAngle semidiam, int ring, Image *color, Point pt, char *s)
{
int d;
d = npixels(semidiam*2);
if(d < 5)
d = 5;
fillellipse(scr, pt, d, d, color, ZP);
if(ring == 1)
ellipse(scr, pt, 5*d/2, d/2, 0, color, ZP);
if(ring == 2)
ellipse(scr, pt, d, d, 0, grey, ZP);
if(s){
d = stringwidth(font, s);
pt.x -= d/2;
pt.y -= font->height/2 + 1;
string(scr, pt, display->black, ZP, font, s);
}
}
void
drawplanet(Image *scr, Planetrec *p, Point pt)
{
if(strcmp(p->name, "sun") == 0){
drawdisc(scr, p->semidiam, 0, suncolor, pt, nil);
return;
}
if(strcmp(p->name, "moon") == 0){
drawdisc(scr, p->semidiam, 0, mooncolor, pt, nil);
return;
}
if(strcmp(p->name, "shadow") == 0){
drawdisc(scr, p->semidiam, 2, shadowcolor, pt, nil);
return;
}
if(strcmp(p->name, "mercury") == 0){
drawdisc(scr, p->semidiam, 0, mercurycolor, pt, "m");
return;
}
if(strcmp(p->name, "venus") == 0){
drawdisc(scr, p->semidiam, 0, venuscolor, pt, "v");
return;
}
if(strcmp(p->name, "mars") == 0){
drawdisc(scr, p->semidiam, 0, marscolor, pt, "M");
return;
}
if(strcmp(p->name, "jupiter") == 0){
drawdisc(scr, p->semidiam, 0, jupitercolor, pt, "J");
return;
}
if(strcmp(p->name, "saturn") == 0){
drawdisc(scr, p->semidiam, 1, saturncolor, pt, "S");
return;
}
if(strcmp(p->name, "uranus") == 0){
drawdisc(scr, p->semidiam, 0, uranuscolor, pt, "U");
return;
}
if(strcmp(p->name, "neptune") == 0){
drawdisc(scr, p->semidiam, 0, neptunecolor, pt, "N");
return;
}
if(strcmp(p->name, "pluto") == 0){
drawdisc(scr, p->semidiam, 0, plutocolor, pt, "P");
return;
}
if(strcmp(p->name, "comet") == 0){
drawdisc(scr, p->semidiam, 0, cometcolor, pt, "C");
return;
}
pt.x -= stringwidth(font, p->name)/2;
pt.y -= font->height/2;
string(scr, pt, grey, ZP, font, p->name);
}
void
tolast(char *name)
{
int i, nlast;
Record *r, rr;
/* stop early to simplify inner loop adjustment */
nlast = 0;
for(i=0,r=rec; i<nrec-nlast; i++,r++)
if(r->type == Planet)
if(name==nil || strcmp(r->u.planet.name, name)==0){
rr = *r;
memmove(rec+i, rec+i+1, (nrec-i-1)*sizeof(Record));
rec[nrec-1] = rr;
--i;
--r;
nlast++;
}
}
int
bbox(int32 extrara, int32 extradec, int quantize)
{
int i;
Record *r;
int ra, dec;
int rah, ram, d1, d2;
double r0;
ramin = 0x7FFFFFFF;
ramax = -0x7FFFFFFF;
decmin = 0x7FFFFFFF;
decmax = -0x7FFFFFFF;
for(i=0,r=rec; i<nrec; i++,r++){
if(r->type == Patch){
radec(r->index, &rah, &ram, &dec);
ra = 15*rah+ram/4;
r0 = c/cos(dec*PI/180);
ra *= c;
dec *= c;
if(dec == 0)
d1 = c, d2 = c;
else if(dec < 0)
d1 = c, d2 = 0;
else
d1 = 0, d2 = c;
}else if(r->type==SAO || r->type==NGC || r->type==Planet || r->type==Abell){
ra = r->u.ngc.ra;
dec = r->u.ngc.dec;
d1 = 0, d2 = 0, r0 = 0;
}else
continue;
if(dec+d2+extradec > decmax)
decmax = dec+d2+extradec;
if(dec-d1-extradec < decmin)
decmin = dec-d1-extradec;
if(folded){
ra -= 180*c;
if(ra < 0)
ra += 360*c;
}
if(ra+r0+extrara > ramax)
ramax = ra+r0+extrara;
if(ra-extrara < ramin)
ramin = ra-extrara;
}
if(decmax > 90*c)
decmax = 90*c;
if(decmin < -90*c)
decmin = -90*c;
if(ramin < 0)
ramin += 360*c;
if(ramax >= 360*c)
ramax -= 360*c;
if(quantize){
/* quantize to degree boundaries */
ramin -= ramin%m5;
if(ramax%m5 != 0)
ramax += m5-(ramax%m5);
if(decmin > 0)
decmin -= decmin%c;
else
decmin -= c - (-decmin)%c;
if(decmax > 0){
if(decmax%c != 0)
decmax += c-(decmax%c);
}else if(decmax < 0){
if(decmax%c != 0)
decmax += ((-decmax)%c);
}
}
if(folded){
if(ramax-ramin > 270*c){
fprint(2, "ra range too wide %ldĀ°\n", (ramax-ramin)/c);
return -1;
}
}else if(ramax-ramin > 270*c){
folded = 1;
return bbox(0, 0, quantize);
}
return 1;
}
int
inbbox(DAngle ra, DAngle dec)
{
int min;
if(dec < decmin)
return 0;
if(dec > decmax)
return 0;
min = ramin;
if(ramin > ramax){ /* straddling 0h0m */
min -= 360*c;
if(ra > 180*c)
ra -= 360*c;
}
if(ra < min)
return 0;
if(ra > ramax)
return 0;
return 1;
}
int
gridra(int32 mapdec)
{
mapdec = abs(mapdec)+c/2;
mapdec /= c;
if(mapdec < 60)
return m5;
if(mapdec < 80)
return 2*m5;
if(mapdec < 85)
return 4*m5;
return 8*m5;
}
#define GREY (nogrey? display->white : grey)
void
plot(char *flags)
{
int i, j, k;
char *t;
int32 x, y;
int ra, dec;
int m;
Point p, pts[10];
Record *r;
Rectangle rect, r1;
int dx, dy, nogrid, textlevel, nogrey, zenithup;
Image *scr;
char *name, buf[32];
double v;
if(plotopen() < 0)
return;
nogrid = 0;
nogrey = 0;
textlevel = 1;
dx = 512;
dy = 512;
zenithup = 0;
for(;;){
if(t = alpha(flags, "nogrid")){
nogrid = 1;
flags = t;
continue;
}
if((t = alpha(flags, "zenith")) || (t = alpha(flags, "zenithup")) ){
zenithup = 1;
flags = t;
continue;
}
if((t = alpha(flags, "notext")) || (t = alpha(flags, "nolabel")) ){
textlevel = 0;
flags = t;
continue;
}
if((t = alpha(flags, "alltext")) || (t = alpha(flags, "alllabel")) ){
textlevel = 2;
flags = t;
continue;
}
if(t = alpha(flags, "dx")){
dx = strtol(t, &t, 0);
if(dx < 100){
fprint(2, "dx %d too small (min 100) in plot\n", dx);
return;
}
flags = skipbl(t);
continue;
}
if(t = alpha(flags, "dy")){
dy = strtol(t, &t, 0);
if(dy < 100){
fprint(2, "dy %d too small (min 100) in plot\n", dy);
return;
}
flags = skipbl(t);
continue;
}
if((t = alpha(flags, "nogrey")) || (t = alpha(flags, "nogray"))){
nogrey = 1;
flags = skipbl(t);
continue;
}
if(*flags){
fprint(2, "syntax error in plot\n");
return;
}
break;
}
flatten();
folded = 0;
if(bbox(0, 0, 1) < 0)
return;
if(ramax-ramin<100 || decmax-decmin<100){
fprint(2, "plot too small\n");
return;
}
scr = allocimage(display, Rect(0, 0, dx, dy), RGB24, 0, DBlack);
if(scr == nil){
fprint(2, "can't allocate image: %r\n");
return;
}
rect = scr->r;
rect.min.x += 16;
rect = insetrect(rect, 40);
if(setmap(ramin, ramax, decmin, decmax, rect, zenithup) < 0){
fprint(2, "can't set up map coordinates\n");
return;
}
if(!nogrid){
for(x=ramin; ; ){
for(j=0; j<nelem(pts); j++){
/* use double to avoid overflow */
v = (double)j / (double)(nelem(pts)-1);
v = decmin + v*(decmax-decmin);
pts[j] = map(x, v);
}
bezspline(scr, pts, nelem(pts), Endsquare, Endsquare, 0, GREY, ZP);
ra = x;
if(folded){
ra -= 180*c;
if(ra < 0)
ra += 360*c;
}
p = pts[0];
p.x -= stringwidth(font, hm5(angle(ra)))/2;
string(scr, p, GREY, ZP, font, hm5(angle(ra)));
p = pts[nelem(pts)-1];
p.x -= stringwidth(font, hm5(angle(ra)))/2;
p.y -= font->height;
string(scr, p, GREY, ZP, font, hm5(angle(ra)));
if(x == ramax)
break;
x += gridra(mapdec);
if(x > ramax)
x = ramax;
}
for(y=decmin; y<=decmax; y+=c){
for(j=0; j<nelem(pts); j++){
/* use double to avoid overflow */
v = (double)j / (double)(nelem(pts)-1);
v = ramin + v*(ramax-ramin);
pts[j] = map(v, y);
}
bezspline(scr, pts, nelem(pts), Endsquare, Endsquare, 0, GREY, ZP);
p = pts[0];
p.x += 3;
p.y -= font->height/2;
string(scr, p, GREY, ZP, font, deg(angle(y)));
p = pts[nelem(pts)-1];
p.x -= 3+stringwidth(font, deg(angle(y)));
p.y -= font->height/2;
string(scr, p, GREY, ZP, font, deg(angle(y)));
}
}
/* reorder to get planets in front of stars */
tolast(nil);
tolast("moon"); /* moon is in front of everything... */
tolast("shadow"); /* ... except the shadow */
for(i=0,r=rec; i<nrec; i++,r++){
dec = r->u.ngc.dec;
ra = r->u.ngc.ra;
if(folded){
ra -= 180*c;
if(ra < 0)
ra += 360*c;
}
if(textlevel){
name = nameof(r);
if(name==nil && textlevel>1 && r->type==SAO){
snprint(buf, sizeof buf, "SAO%ld", r->index);
name = buf;
}
if(name)
drawname(scr, nogrey? display->white : alphagrey, name, ra, dec);
}
if(r->type == Planet){
drawplanet(scr, &r->u.planet, map(ra, dec));
continue;
}
if(r->type == SAO){
m = r->u.sao.mag;
if(m == UNKNOWNMAG)
m = r->u.sao.mpg;
if(m == UNKNOWNMAG)
continue;
m = dsize(m);
if(m < 3)
fillellipse(scr, map(ra, dec), m, m, nogrey? display->white : lightgrey, ZP);
else{
ellipse(scr, map(ra, dec), m+1, m+1, 0, display->black, ZP);
fillellipse(scr, map(ra, dec), m, m, display->white, ZP);
}
continue;
}
if(r->type == Abell){
ellipse(scr, addpt(map(ra, dec), Pt(-3, 2)), 2, 1, 0, lightblue, ZP);
ellipse(scr, addpt(map(ra, dec), Pt(3, 2)), 2, 1, 0, lightblue, ZP);
ellipse(scr, addpt(map(ra, dec), Pt(0, -2)), 1, 2, 0, lightblue, ZP);
continue;
}
switch(r->u.ngc.type){
case Galaxy:
j = npixels(r->u.ngc.diam);
if(j < 4)
j = 4;
if(j > 10)
k = j/3;
else
k = j/2;
ellipse(scr, map(ra, dec), j, k, 0, lightblue, ZP);
break;
case PlanetaryN:
p = map(ra, dec);
j = npixels(r->u.ngc.diam);
if(j < 3)
j = 3;
ellipse(scr, p, j, j, 0, green, ZP);
line(scr, Pt(p.x, p.y+j+1), Pt(p.x, p.y+j+4),
Endsquare, Endsquare, 0, green, ZP);
line(scr, Pt(p.x, p.y-(j+1)), Pt(p.x, p.y-(j+4)),
Endsquare, Endsquare, 0, green, ZP);
line(scr, Pt(p.x+j+1, p.y), Pt(p.x+j+4, p.y),
Endsquare, Endsquare, 0, green, ZP);
line(scr, Pt(p.x-(j+1), p.y), Pt(p.x-(j+4), p.y),
Endsquare, Endsquare, 0, green, ZP);
break;
case DiffuseN:
case NebularCl:
p = map(ra, dec);
j = npixels(r->u.ngc.diam);
if(j < 4)
j = 4;
r1.min = Pt(p.x-j, p.y-j);
r1.max = Pt(p.x+j, p.y+j);
if(r->u.ngc.type != DiffuseN)
draw(scr, r1, ocstipple, ocstipple, ZP);
line(scr, Pt(p.x-j, p.y-j), Pt(p.x+j, p.y-j),
Endsquare, Endsquare, 0, green, ZP);
line(scr, Pt(p.x-j, p.y+j), Pt(p.x+j, p.y+j),
Endsquare, Endsquare, 0, green, ZP);
line(scr, Pt(p.x-j, p.y-j), Pt(p.x-j, p.y+j),
Endsquare, Endsquare, 0, green, ZP);
line(scr, Pt(p.x+j, p.y-j), Pt(p.x+j, p.y+j),
Endsquare, Endsquare, 0, green, ZP);
break;
case OpenCl:
p = map(ra, dec);
j = npixels(r->u.ngc.diam);
if(j < 4)
j = 4;
fillellipse(scr, p, j, j, ocstipple, ZP);
break;
case GlobularCl:
j = npixels(r->u.ngc.diam);
if(j < 4)
j = 4;
p = map(ra, dec);
ellipse(scr, p, j, j, 0, lightgrey, ZP);
line(scr, Pt(p.x-(j-1), p.y), Pt(p.x+j, p.y),
Endsquare, Endsquare, 0, lightgrey, ZP);
line(scr, Pt(p.x, p.y-(j-1)), Pt(p.x, p.y+j),
Endsquare, Endsquare, 0, lightgrey, ZP);
break;
}
}
flushimage(display, 1);
displayimage(scr);
}
int
runcommand(char *command, int p[2])
{
switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
case -1:
return -1;
default:
break;
case 0:
dup(p[1], 1);
close(p[0]);
close(p[1]);
execlp("rc", "rc", "-c", command, nil);
fprint(2, "can't exec %s: %r", command);
exits(nil);
}
return 1;
}
void
parseplanet(char *line, Planetrec *p)
{
char *fld[6];
int i, nfld;
char *s;
if(line[0] == '\0')
return;
line[10] = '\0'; /* terminate name */
s = strrchr(line, ' ');
if(s == nil)
s = line;
else
s++;
strcpy(p->name, s);
for(i=0; s[i]!='\0'; i++)
p->name[i] = tolower(s[i]);
p->name[i] = '\0';
nfld = getfields(line+11, fld, nelem(fld), 1, " ");
p->ra = dangle(getra(fld[0]));
p->dec = dangle(getra(fld[1]));
p->az = atof(fld[2])*MILLIARCSEC;
p->alt = atof(fld[3])*MILLIARCSEC;
p->semidiam = atof(fld[4])*1000;
if(nfld > 5)
p->phase = atof(fld[5]);
else
p->phase = 0;
}
void
astro(char *flags, int initial)
{
int p[2];
int i, n, np;
char cmd[256], buf[4096], *lines[20], *fld[10];
snprint(cmd, sizeof cmd, "astro -p %s", flags);
if(pipe(p) < 0){
fprint(2, "can't pipe: %r\n");
return;
}
if(runcommand(cmd, p) < 0){
close(p[0]);
close(p[1]);
fprint(2, "can't run astro: %r");
return;
}
close(p[1]);
n = readn(p[0], buf, sizeof buf-1);
if(n <= 0){
fprint(2, "no data from astro\n");
return;
}
if(!initial)
Bwrite(&bout, buf, n);
buf[n] = '\0';
np = getfields(buf, lines, nelem(lines), 0, "\n");
if(np <= 1){
fprint(2, "astro: not enough output\n");
return;
}
Bprint(&bout, "%s\n", lines[0]);
Bflush(&bout);
/* get latitude and longitude */
if(getfields(lines[0], fld, nelem(fld), 1, " ") < 8)
fprint(2, "astro: can't read longitude: too few fields\n");
else{
mysid = getra(fld[5])*180./PI;
mylat = getra(fld[6])*180./PI;
mylon = getra(fld[7])*180./PI;
}
/*
* Each time we run astro, we generate a new planet list
* so multiple appearances of a planet may exist as we plot
* its motion over time.
*/
planet = malloc(NPlanet*sizeof planet[0]);
if(planet == nil){
fprint(2, "astro: malloc failed: %r\n");
exits("malloc");
}
memset(planet, 0, NPlanet*sizeof planet[0]);
for(i=1; i<np; i++)
parseplanet(lines[i], &planet[i-1]);
}