aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/scat/scat.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/scat/scat.c')
-rw-r--r--src/cmd/scat/scat.c1671
1 files changed, 1671 insertions, 0 deletions
diff --git a/src/cmd/scat/scat.c b/src/cmd/scat/scat.c
new file mode 100644
index 00000000..9579706a
--- /dev/null
+++ b/src/cmd/scat/scat.c
@@ -0,0 +1,1671 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include "sky.h"
+#include "strings.c"
+
+enum
+{
+ NNGC=7840, /* number of NGC numbers [1..NNGC] */
+ NIC = 5386, /* number of IC numbers */
+ NNGCrec=NNGC+NIC, /* number of records in the NGC catalog (including IC's, starting at NNGC */
+ NMrec=122, /* number of M records */
+ NM=110, /* number of M numbers */
+ NAbell=2712, /* number of records in the Abell catalog */
+ NName=1000, /* number of prose names; estimated maximum (read from editable text file) */
+ NBayer=1517, /* number of bayer entries */
+ NSAO=258998, /* number of SAO stars */
+ MAXcon=1932, /* maximum number of patches in a constellation */
+ Ncon=88, /* number of constellations */
+ Npatch=92053, /* highest patch number */
+};
+
+char ngctype[NNGCrec];
+Mindexrec mindex[NMrec];
+Namerec name[NName];
+Bayerec bayer[NBayer];
+long con[MAXcon];
+ushort conindex[Ncon+1];
+long patchaddr[Npatch+1];
+
+Record *rec;
+Record *orec;
+Record *cur;
+
+char *dir;
+int saodb;
+int ngcdb;
+int abelldb;
+int ngctypedb;
+int mindexdb;
+int namedb;
+int bayerdb;
+int condb;
+int conindexdb;
+int patchdb;
+char parsed[3];
+long nrec;
+long nreca;
+long norec;
+long noreca;
+
+Biobuf bin;
+Biobuf bout;
+
+void
+main(int argc, char *argv[])
+{
+ char *line;
+
+ dir = unsharp(DIR);
+ Binit(&bin, 0, OREAD);
+ Binit(&bout, 1, OWRITE);
+ if(argc != 1)
+ dir = argv[1];
+ astro("", 1);
+ while(line = Brdline(&bin, '\n')){
+ line[Blinelen(&bin)-1] = 0;
+ lookup(line, 1);
+ Bflush(&bout);
+ }
+ if(display != nil){
+ closedisplay(display);
+ /* automatic refresh of rio window is triggered by mouse */
+ // close(open("/dev/mouse", OREAD));
+ }
+ return;
+}
+
+void
+reset(void)
+{
+ nrec = 0;
+ cur = rec;
+}
+
+void
+grow(void)
+{
+ nrec++;
+ if(nreca < nrec){
+ nreca = nrec+50;
+ rec = realloc(rec, nreca*sizeof(Record));
+ if(rec == 0){
+ fprint(2, "scat: realloc fails\n");
+ exits("realloc");
+ }
+ }
+ cur = rec+nrec-1;
+}
+
+void
+copy(void)
+{
+ if(noreca < nreca){
+ noreca = nreca;
+ orec = realloc(orec, nreca*sizeof(Record));
+ if(orec == 0){
+ fprint(2, "scat: realloc fails\n");
+ exits("realloc");
+ }
+ }
+ memmove(orec, rec, nrec*sizeof(Record));
+ norec = nrec;
+}
+
+int
+eopen(char *s)
+{
+ char buf[128];
+ int f;
+
+ sprint(buf, "%s/%s.scat", dir, s);
+ f = open(buf, 0);
+ if(f<0){
+ fprint(2, "scat: can't open %s\n", buf);
+ exits("open");
+ }
+ return f;
+}
+
+
+void
+Eread(int f, char *name, void *addr, long n)
+{
+ if(read(f, addr, n) != n){ /* BUG! */
+ fprint(2, "scat: read error on %s\n", name);
+ exits("read");
+ }
+}
+
+char*
+skipbl(char *s)
+{
+ while(*s!=0 && (*s==' ' || *s=='\t'))
+ s++;
+ return s;
+}
+
+char*
+skipstr(char *s, char *t)
+{
+ while(*s && *s==*t)
+ s++, t++;
+ return skipbl(s);
+}
+
+/* produce little-endian long at address l */
+long
+Long(long *l)
+{
+ uchar *p;
+
+ p = (uchar*)l;
+ return (long)p[0]|((long)p[1]<<8)|((long)p[2]<<16)|((long)p[3]<<24);
+}
+
+/* produce little-endian long at address l */
+int
+Short(short *s)
+{
+ uchar *p;
+
+ p = (uchar*)s;
+ return p[0]|(p[1]<<8);
+}
+
+void
+nameopen(void)
+{
+ Biobuf b;
+ int i;
+ char *l, *p;
+
+ if(namedb == 0){
+ namedb = eopen("name");
+ Binit(&b, namedb, OREAD);
+ for(i=0; i<NName; i++){
+ l = Brdline(&b, '\n');
+ if(l == 0)
+ break;
+ p = strchr(l, '\t');
+ if(p == 0){
+ Badformat:
+ Bprint(&bout, "warning: name.scat bad format; line %d\n", i+1);
+ break;
+ }
+ *p++ = 0;
+ strcpy(name[i].name, l);
+ if(strncmp(p, "ngc", 3) == 0)
+ name[i].ngc = atoi(p+3);
+ else if(strncmp(p, "ic", 2) == 0)
+ name[i].ngc = atoi(p+2)+NNGC;
+ else if(strncmp(p, "sao", 3) == 0)
+ name[i].sao = atoi(p+3);
+ else if(strncmp(p, "abell", 5) == 0)
+ name[i].abell = atoi(p+5);
+ else
+ goto Badformat;
+ }
+ if(i == NName)
+ Bprint(&bout, "warning: too many names in name.scat (max %d); extra ignored\n", NName);
+ close(namedb);
+
+ bayerdb = eopen("bayer");
+ Eread(bayerdb, "bayer", bayer, sizeof bayer);
+ close(bayerdb);
+ for(i=0; i<NBayer; i++)
+ bayer[i].sao = Long(&bayer[i].sao);
+ }
+}
+
+void
+saoopen(void)
+{
+ if(saodb == 0){
+ nameopen();
+ saodb = eopen("sao");
+ }
+}
+
+void
+ngcopen(void)
+{
+ if(ngcdb == 0){
+ nameopen();
+ ngcdb = eopen("ngc2000");
+ ngctypedb = eopen("ngc2000type");
+ Eread(ngctypedb, "ngctype", ngctype, sizeof ngctype);
+ close(ngctypedb);
+ }
+}
+
+void
+abellopen(void)
+{
+ /* nothing extra to do with abell: it's directly indexed by number */
+ if(abelldb == 0)
+ abelldb = eopen("abell");
+}
+
+void
+patchopen(void)
+{
+ Biobuf *b;
+ long l, m;
+ char buf[100];
+
+ if(patchdb == 0){
+ patchdb = eopen("patch");
+ sprint(buf, "%s/patchindex.scat", dir);
+ b = Bopen(buf, OREAD);
+ if(b == 0){
+ fprint(2, "can't open %s\n", buf);
+ exits("open");
+ }
+ for(m=0,l=0; l<=Npatch; l++)
+ patchaddr[l] = m += Bgetc(b)*4;
+ Bterm(b);
+ }
+}
+
+void
+mopen(void)
+{
+ int i;
+
+ if(mindexdb == 0){
+ mindexdb = eopen("mindex");
+ Eread(mindexdb, "mindex", mindex, sizeof mindex);
+ close(mindexdb);
+ for(i=0; i<NMrec; i++)
+ mindex[i].ngc = Short(&mindex[i].ngc);
+ }
+}
+
+void
+constelopen(void)
+{
+ int i;
+
+ if(condb == 0){
+ condb = eopen("con");
+ conindexdb = eopen("conindex");
+ Eread(conindexdb, "conindex", conindex, sizeof conindex);
+ close(conindexdb);
+ for(i=0; i<Ncon+1; i++)
+ conindex[i] = Short((short*)&conindex[i]);
+ }
+}
+
+void
+lowercase(char *s)
+{
+ for(; *s; s++)
+ if('A'<=*s && *s<='Z')
+ *s += 'a'-'A';
+}
+
+int
+loadngc(long index)
+{
+ static int failed;
+ long j;
+
+ ngcopen();
+ j = (index-1)*sizeof(NGCrec);
+ grow();
+ cur->type = NGC;
+ cur->index = index;
+ seek(ngcdb, j, 0);
+ /* special case: NGC data may not be available */
+ if(read(ngcdb, &cur->ngc, sizeof(NGCrec)) != sizeof(NGCrec)){
+ if(!failed){
+ fprint(2, "scat: NGC database not available\n");
+ failed++;
+ }
+ cur->type = NONGC;
+ cur->ngc.ngc = 0;
+ cur->ngc.ra = 0;
+ cur->ngc.dec = 0;
+ cur->ngc.diam = 0;
+ cur->ngc.mag = 0;
+ return 0;
+ }
+ cur->ngc.ngc = Short(&cur->ngc.ngc);
+ cur->ngc.ra = Long(&cur->ngc.ra);
+ cur->ngc.dec = Long(&cur->ngc.dec);
+ cur->ngc.diam = Long(&cur->ngc.diam);
+ cur->ngc.mag = Short(&cur->ngc.mag);
+ return 1;
+}
+
+int
+loadabell(long index)
+{
+ long j;
+
+ abellopen();
+ j = index-1;
+ grow();
+ cur->type = Abell;
+ cur->index = index;
+ seek(abelldb, j*sizeof(Abellrec), 0);
+ Eread(abelldb, "abell", &cur->abell, sizeof(Abellrec));
+ cur->abell.abell = Short(&cur->abell.abell);
+ if(cur->abell.abell != index){
+ fprint(2, "bad format in abell catalog\n");
+ exits("abell");
+ }
+ cur->abell.ra = Long(&cur->abell.ra);
+ cur->abell.dec = Long(&cur->abell.dec);
+ cur->abell.glat = Long(&cur->abell.glat);
+ cur->abell.glong = Long(&cur->abell.glong);
+ cur->abell.rad = Long(&cur->abell.rad);
+ cur->abell.mag10 = Short(&cur->abell.mag10);
+ cur->abell.pop = Short(&cur->abell.pop);
+ cur->abell.dist = Short(&cur->abell.dist);
+ return 1;
+}
+
+int
+loadsao(int index)
+{
+ if(index<=0 || index>NSAO)
+ return 0;
+ saoopen();
+ grow();
+ cur->type = SAO;
+ cur->index = index;
+ seek(saodb, (index-1)*sizeof(SAOrec), 0);
+ Eread(saodb, "sao", &cur->sao, sizeof(SAOrec));
+ cur->sao.ra = Long(&cur->sao.ra);
+ cur->sao.dec = Long(&cur->sao.dec);
+ cur->sao.dra = Long(&cur->sao.dra);
+ cur->sao.ddec = Long(&cur->sao.ddec);
+ cur->sao.mag = Short(&cur->sao.mag);
+ cur->sao.mpg = Short(&cur->sao.mpg);
+ cur->sao.hd = Long(&cur->sao.hd);
+ return 1;
+}
+
+int
+loadplanet(int index, Record *r)
+{
+ if(index<0 || index>NPlanet || planet[index].name[0]=='\0')
+ return 0;
+ grow();
+ cur->type = Planet;
+ cur->index = index;
+ /* check whether to take new or existing record */
+ if(r == nil)
+ memmove(&cur->planet, &planet[index], sizeof(Planetrec));
+ else
+ memmove(&cur->planet, &r->planet, sizeof(Planetrec));
+ return 1;
+}
+
+int
+loadpatch(long index)
+{
+ int i;
+
+ patchopen();
+ if(index<=0 || index>Npatch)
+ return 0;
+ grow();
+ cur->type = Patch;
+ cur->index = index;
+ seek(patchdb, patchaddr[index-1], 0);
+ cur->patch.nkey = (patchaddr[index]-patchaddr[index-1])/4;
+ Eread(patchdb, "patch", cur->patch.key, cur->patch.nkey*4);
+ for(i=0; i<cur->patch.nkey; i++)
+ cur->patch.key[i] = Long(&cur->patch.key[i]);
+ return 1;
+}
+
+int
+loadtype(int t)
+{
+ int i;
+
+ ngcopen();
+ for(i=0; i<NNGCrec; i++)
+ if(t == (ngctype[i])){
+ grow();
+ cur->type = NGCN;
+ cur->index = i+1;
+ }
+ return 1;
+}
+
+void
+flatten(void)
+{
+ int i, j, notflat;
+ Record *or;
+ long key;
+
+ loop:
+ copy();
+ reset();
+ notflat = 0;
+ for(i=0,or=orec; i<norec; i++,or++){
+ switch(or->type){
+ default:
+ fprint(2, "bad type %d in flatten\n", or->type);
+ break;
+
+ case NONGC:
+ break;
+
+ case Planet:
+ case Abell:
+ case NGC:
+ case SAO:
+ grow();
+ memmove(cur, or, sizeof(Record));
+ break;
+
+ case NGCN:
+ if(loadngc(or->index))
+ notflat = 1;
+ break;
+
+ case NamedSAO:
+ loadsao(or->index);
+ notflat = 1;
+ break;
+
+ case NamedNGC:
+ if(loadngc(or->index))
+ notflat = 1;
+ break;
+
+ case NamedAbell:
+ loadabell(or->index);
+ notflat = 1;
+ break;
+
+ case PatchC:
+ loadpatch(or->index);
+ notflat = 1;
+ break;
+
+ case Patch:
+ for(j=1; j<or->patch.nkey; j++){
+ key = or->patch.key[j];
+ if((key&0x3F) == SAO)
+ loadsao((key>>8)&0xFFFFFF);
+ else if((key&0x3F) == Abell)
+ loadabell((key>>8)&0xFFFFFF);
+ else
+ loadngc((key>>16)&0xFFFF);
+ }
+ break;
+ }
+ }
+ if(notflat)
+ goto loop;
+}
+
+int
+ism(int index)
+{
+ int i;
+
+ for(i=0; i<NMrec; i++)
+ if(mindex[i].ngc == index)
+ return 1;
+ return 0;
+}
+
+char*
+alpha(char *s, char *t)
+{
+ int n;
+
+ n = strlen(t);
+ if(strncmp(s, t, n)==0 && (s[n]<'a' || 'z'<s[n]))
+ return skipbl(s+n);
+ return 0;
+
+}
+
+char*
+text(char *s, char *t)
+{
+ int n;
+
+ n = strlen(t);
+ if(strncmp(s, t, n)==0 && (s[n]==0 || s[n]==' ' || s[n]=='\t'))
+ return skipbl(s+n);
+ return 0;
+
+}
+
+int
+cull(char *s, int keep, int dobbox)
+{
+ int i, j, nobj, keepthis;
+ Record *or;
+ char *t;
+ int dogrtr, doless, dom, dosao, dongc, doabell;
+ int mgrtr, mless;
+ char obj[100];
+
+ memset(obj, 0, sizeof(obj));
+ nobj = 0;
+ dogrtr = 0;
+ doless = 0;
+ dom = 0;
+ dongc = 0;
+ dosao = 0;
+ doabell = 0;
+ mgrtr = mless= 0;
+ if(dobbox)
+ goto Cull;
+ for(;;){
+ if(s[0] == '>'){
+ dogrtr = 1;
+ mgrtr = 10 * strtod(s+1, &t);
+ if(mgrtr==0 && t==s+1){
+ fprint(2, "bad magnitude\n");
+ return 0;
+ }
+ s = skipbl(t);
+ continue;
+ }
+ if(s[0] == '<'){
+ doless = 1;
+ mless = 10 * strtod(s+1, &t);
+ if(mless==0 && t==s+1){
+ fprint(2, "bad magnitude\n");
+ return 0;
+ }
+ s = skipbl(t);
+ continue;
+ }
+ if(t = text(s, "m")){
+ dom = 1;
+ s = t;
+ continue;
+ }
+ if(t = text(s, "sao")){
+ dosao = 1;
+ s = t;
+ continue;
+ }
+ if(t = text(s, "ngc")){
+ dongc = 1;
+ s = t;
+ continue;
+ }
+ if(t = text(s, "abell")){
+ doabell = 1;
+ s = t;
+ continue;
+ }
+ for(i=0; names[i].name; i++)
+ if(t = alpha(s, names[i].name)){
+ if(nobj > 100){
+ fprint(2, "too many object types\n");
+ return 0;
+ }
+ obj[nobj++] = names[i].type;
+ s = t;
+ goto Continue;
+ }
+ break;
+ Continue:;
+ }
+ if(*s){
+ fprint(2, "syntax error in object list\n");
+ return 0;
+ }
+
+ Cull:
+ flatten();
+ copy();
+ reset();
+ if(dom)
+ mopen();
+ if(dosao)
+ saoopen();
+ if(dongc || nobj)
+ ngcopen();
+ if(doabell)
+ abellopen();
+ for(i=0,or=orec; i<norec; i++,or++){
+ keepthis = !keep;
+ if(dobbox && inbbox(or->ngc.ra, or->ngc.dec))
+ keepthis = keep;
+ if(doless && or->ngc.mag <= mless)
+ keepthis = keep;
+ if(dogrtr && or->ngc.mag >= mgrtr)
+ keepthis = keep;
+ if(dom && (or->type==NGC && ism(or->ngc.ngc)))
+ keepthis = keep;
+ if(dongc && or->type==NGC)
+ keepthis = keep;
+ if(doabell && or->type==Abell)
+ keepthis = keep;
+ if(dosao && or->type==SAO)
+ keepthis = keep;
+ for(j=0; j<nobj; j++)
+ if(or->type==NGC && or->ngc.type==obj[j])
+ keepthis = keep;
+ if(keepthis){
+ grow();
+ memmove(cur, or, sizeof(Record));
+ }
+ }
+ return 1;
+}
+
+int
+compar(const void *va, const void *vb)
+{
+ Record *a=(Record*)va, *b=(Record*)vb;
+
+ if(a->type == b->type)
+ return a->index - b->index;
+ return a->type - b->type;
+}
+
+void
+sort(void)
+{
+ int i;
+ Record *r, *s;
+
+ if(nrec == 0)
+ return;
+ qsort(rec, nrec, sizeof(Record), compar);
+ r = rec+1;
+ s = rec;
+ for(i=1; i<nrec; i++,r++){
+ /* may have multiple instances of a planet in the scene */
+ if(r->type==s->type && r->index==s->index && r->type!=Planet)
+ continue;
+ memmove(++s, r, sizeof(Record));
+ }
+ nrec = (s+1)-rec;
+}
+
+char greekbuf[128];
+
+char*
+togreek(char *s)
+{
+ char *t;
+ int i, n;
+ Rune r;
+
+ t = greekbuf;
+ while(*s){
+ for(i=1; i<=24; i++){
+ n = strlen(greek[i]);
+ if(strncmp(s, greek[i], n)==0 && (s[n]==' ' || s[n]=='\t')){
+ s += n;
+ t += runetochar(t, &greeklet[i]);
+ goto Cont;
+ }
+ }
+ n = chartorune(&r, s);
+ for(i=0; i<n; i++)
+ *t++ = *s++;
+ Cont:;
+ }
+ *t = 0;
+ return greekbuf;
+}
+
+char*
+fromgreek(char *s)
+{
+ char *t;
+ int i, n;
+ Rune r;
+
+ t = greekbuf;
+ while(*s){
+ n = chartorune(&r, s);
+ for(i=1; i<=24; i++){
+ if(r == greeklet[i]){
+ strcpy(t, greek[i]);
+ t += strlen(greek[i]);
+ s += n;
+ goto Cont;
+ }
+ }
+ for(i=0; i<n; i++)
+ *t++ = *s++;
+ Cont:;
+ }
+ *t = 0;
+ return greekbuf;
+}
+
+#ifdef OLD
+/*
+ * Old version
+ */
+int
+coords(int deg)
+{
+ int i;
+ int x, y;
+ Record *or;
+ long dec, ra, ndec, nra;
+ int rdeg;
+
+ flatten();
+ copy();
+ reset();
+ deg *= 2;
+ for(i=0,or=orec; i<norec; i++,or++){
+ if(or->type == Planet) /* must keep it here */
+ loadplanet(or->index, or);
+ dec = or->ngc.dec/MILLIARCSEC;
+ ra = or->ngc.ra/MILLIARCSEC;
+ rdeg = deg/cos((dec*PI)/180);
+ for(y=-deg; y<=+deg; y++){
+ ndec = dec*2+y;
+ if(ndec/2>=90 || ndec/2<=-90)
+ continue;
+ /* fp errors hurt here, so we round 1' to the pole */
+ if(ndec >= 0)
+ ndec = ndec*500*60*60 + 60000;
+ else
+ ndec = ndec*500*60*60 - 60000;
+ for(x=-rdeg; x<=+rdeg; x++){
+ nra = ra*2+x;
+ if(nra/2 < 0)
+ nra += 360*2;
+ if(nra/2 >= 360)
+ nra -= 360*2;
+ /* fp errors hurt here, so we round up 1' */
+ nra = nra/2*MILLIARCSEC + 60000;
+ loadpatch(patcha(angle(nra), angle(ndec)));
+ }
+ }
+ }
+ sort();
+ return 1;
+}
+#endif
+
+/*
+ * New version attempts to match the boundaries of the plot better.
+ */
+int
+coords(int deg)
+{
+ int i;
+ int x, y, xx;
+ Record *or;
+ long min, circle;
+ double factor;
+
+ flatten();
+ circle = 360*MILLIARCSEC;
+ deg *= MILLIARCSEC;
+
+ /* find center */
+ folded = 0;
+ bbox(0, 0, 0);
+ /* now expand */
+ factor = cos(angle((decmax+decmin)/2));
+ if(factor < .2)
+ factor = .2;
+ factor = floor(1/factor);
+ folded = 0;
+ bbox(factor*deg, deg, 1);
+ Bprint(&bout, "%s to ", hms(angle(ramin)));
+ Bprint(&bout, "%s\n", hms(angle(ramax)));
+ Bprint(&bout, "%s to ", dms(angle(decmin)));
+ Bprint(&bout, "%s\n", dms(angle(decmax)));
+ copy();
+ reset();
+ for(i=0,or=orec; i<norec; i++,or++)
+ if(or->type == Planet) /* must keep it here */
+ loadplanet(or->index, or);
+ min = ramin;
+ if(ramin > ramax)
+ min -= circle;
+ for(x=min; x<=ramax; x+=250*60*60){
+ xx = x;
+ if(xx < 0)
+ xx += circle;
+ for(y=decmin; y<=decmax; y+=250*60*60)
+ if(-circle/4 < y && y < circle/4)
+ loadpatch(patcha(angle(xx), angle(y)));
+ }
+ sort();
+ cull(nil, 1, 1);
+ return 1;
+}
+
+void
+pplate(char *flags)
+{
+ int i;
+ long c;
+ int na, rah, ram, d1, d2;
+ double r0;
+ int ra, dec;
+ long ramin, ramax, decmin, decmax; /* all in degrees */
+ Record *r;
+ int folded;
+ Angle racenter, deccenter, rasize, decsize, a[4];
+ Picture *pic;
+
+ rasize = -1.0;
+ decsize = -1.0;
+ na = 0;
+ for(;;){
+ while(*flags==' ')
+ flags++;
+ if(('0'<=*flags && *flags<='9') || *flags=='+' || *flags=='-'){
+ if(na >= 3)
+ goto err;
+ a[na++] = getra(flags);
+ while(*flags && *flags!=' ')
+ flags++;
+ continue;
+ }
+ if(*flags){
+ err:
+ Bprint(&bout, "syntax error in plate\n");
+ return;
+ }
+ break;
+ }
+ switch(na){
+ case 0:
+ break;
+ case 1:
+ rasize = a[0];
+ decsize = rasize;
+ break;
+ case 2:
+ rasize = a[0];
+ decsize = a[1];
+ break;
+ case 3:
+ case 4:
+ racenter = a[0];
+ deccenter = a[1];
+ rasize = a[2];
+ if(na == 4)
+ decsize = a[3];
+ else
+ decsize = rasize;
+ if(rasize<0.0 || decsize<0.0){
+ Bprint(&bout, "negative sizes\n");
+ return;
+ }
+ goto done;
+ }
+ folded = 0;
+ /* convert to milliarcsec */
+ c = 1000*60*60;
+ Again:
+ if(nrec == 0){
+ Bprint(&bout, "empty\n");
+ return;
+ }
+ ramin = 0x7FFFFFFF;
+ ramax = -0x7FFFFFFF;
+ decmin = 0x7FFFFFFF;
+ decmax = -0x7FFFFFFF;
+ for(r=rec,i=0; i<nrec; i++,r++){
+ if(r->type == Patch){
+ radec(r->index, &rah, &ram, &dec);
+ ra = 15*rah+ram/4;
+ r0 = c/cos(RAD(dec));
+ 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==Abell){
+ ra = r->ngc.ra;
+ dec = r->ngc.dec;
+ d1 = 0, d2 = 0, r0 = 0;
+ }else if(r->type==NGCN){
+ loadngc(r->index);
+ continue;
+ }else if(r->type==NamedSAO){
+ loadsao(r->index);
+ continue;
+ }else if(r->type==NamedNGC){
+ loadngc(r->index);
+ continue;
+ }else if(r->type==NamedAbell){
+ loadabell(r->index);
+ continue;
+ }else
+ continue;
+ if(dec+d2 > decmax)
+ decmax = dec+d2;
+ if(dec-d1 < decmin)
+ decmin = dec-d1;
+ if(folded){
+ ra -= 180*c;
+ if(ra < 0)
+ ra += 360*c;
+ }
+ if(ra+r0 > ramax)
+ ramax = ra+r0;
+ if(ra < ramin)
+ ramin = ra;
+ }
+ if(!folded && ramax-ramin>270*c){
+ folded = 1;
+ goto Again;
+ }
+ racenter = angle(ramin+(ramax-ramin)/2);
+ deccenter = angle(decmin+(decmax-decmin)/2);
+ if(rasize<0 || decsize<0){
+ rasize = angle(ramax-ramin)*cos(deccenter);
+ decsize = angle(decmax-decmin);
+ }
+ done:
+ if(DEG(rasize)>1.1 || DEG(decsize)>1.1){
+ Bprint(&bout, "plate too big: %s", ms(rasize));
+ Bprint(&bout, " x %s\n", ms(decsize));
+ Bprint(&bout, "trimming to 30'x30'\n");
+ rasize = RAD(0.5);
+ decsize = RAD(0.5);
+ }
+ Bprint(&bout, "%s %s ", hms(racenter), dms(deccenter));
+ Bprint(&bout, "%s", ms(rasize));
+ Bprint(&bout, " x %s\n", ms(decsize));
+ Bflush(&bout);
+ flatten();
+ pic = image(racenter, deccenter, rasize, decsize);
+ if(pic == 0)
+ return;
+ Bprint(&bout, "plate %s locn %d %d %d %d\n", pic->name, pic->minx, pic->miny, pic->maxx, pic->maxy);
+ Bflush(&bout);
+ displaypic(pic);
+}
+
+void
+lookup(char *s, int doreset)
+{
+ int i, j, k;
+ int rah, ram, deg;
+ char *starts, *inputline=s, *t, *u;
+ Record *r;
+ Rune c;
+ long n;
+ double x;
+ Angle ra;
+
+ lowercase(s);
+ s = skipbl(s);
+
+ if(*s == 0)
+ goto Print;
+
+ if(t = alpha(s, "flat")){
+ if(*t){
+ fprint(2, "flat takes no arguments\n");
+ return;
+ }
+ if(nrec == 0){
+ fprint(2, "no records\n");
+ return;
+ }
+ flatten();
+ goto Print;
+ }
+
+ if(t = alpha(s, "print")){
+ if(*t){
+ fprint(2, "print takes no arguments\n");
+ return;
+ }
+ for(i=0,r=rec; i<nrec; i++,r++)
+ prrec(r);
+ return;
+ }
+
+ if(t = alpha(s, "add")){
+ lookup(t, 0);
+ return;
+ }
+
+ if(t = alpha(s, "sao")){
+ n = strtoul(t, &u, 10);
+ if(n<=0 || n>NSAO)
+ goto NotFound;
+ t = skipbl(u);
+ if(*t){
+ fprint(2, "syntax error in sao\n");
+ return;
+ }
+ if(doreset)
+ reset();
+ if(!loadsao(n))
+ goto NotFound;
+ goto Print;
+ }
+
+ if(t = alpha(s, "ngc")){
+ n = strtoul(t, &u, 10);
+ if(n<=0 || n>NNGC)
+ goto NotFound;
+ t = skipbl(u);
+ if(*t){
+ fprint(2, "syntax error in ngc\n");
+ return;
+ }
+ if(doreset)
+ reset();
+ if(!loadngc(n))
+ goto NotFound;
+ goto Print;
+ }
+
+ if(t = alpha(s, "ic")){
+ n = strtoul(t, &u, 10);
+ if(n<=0 || n>NIC)
+ goto NotFound;
+ t = skipbl(u);
+ if(*t){
+ fprint(2, "syntax error in ic\n");
+ return;
+ }
+ if(doreset)
+ reset();
+ if(!loadngc(n+NNGC))
+ goto NotFound;
+ goto Print;
+ }
+
+ if(t = alpha(s, "abell")){
+ n = strtoul(t, &u, 10);
+ if(n<=0 || n>NAbell)
+ goto NotFound;
+ if(doreset)
+ reset();
+ if(!loadabell(n))
+ goto NotFound;
+ goto Print;
+ }
+
+ if(t = alpha(s, "m")){
+ n = strtoul(t, &u, 10);
+ if(n<=0 || n>NM)
+ goto NotFound;
+ mopen();
+ for(j=n-1; mindex[j].m<n; j++)
+ ;
+ if(doreset)
+ reset();
+ while(mindex[j].m == n){
+ if(mindex[j].ngc){
+ grow();
+ cur->type = NGCN;
+ cur->index = mindex[j].ngc;
+ }
+ j++;
+ }
+ goto Print;
+ }
+
+ for(i=1; i<=Ncon; i++)
+ if(t = alpha(s, constel[i])){
+ if(*t){
+ fprint(2, "syntax error in constellation\n");
+ return;
+ }
+ constelopen();
+ seek(condb, 4L*conindex[i-1], 0);
+ j = conindex[i]-conindex[i-1];
+ Eread(condb, "con", con, 4*j);
+ if(doreset)
+ reset();
+ for(k=0; k<j; k++){
+ grow();
+ cur->type = PatchC;
+ cur->index = Long(&con[k]);
+ }
+ goto Print;
+ }
+
+ if(t = alpha(s, "expand")){
+ n = 0;
+ if(*t){
+ if(*t<'0' && '9'<*t){
+ Expanderr:
+ fprint(2, "syntax error in expand\n");
+ return;
+ }
+ n = strtoul(t, &u, 10);
+ t = skipbl(u);
+ if(*t)
+ goto Expanderr;
+ }
+ coords(n);
+ goto Print;
+ }
+
+ if(t = alpha(s, "plot")){
+ if(nrec == 0){
+ Bprint(&bout, "empty\n");
+ return;
+ }
+ plot(t);
+ return;
+ }
+
+ if(t = alpha(s, "astro")){
+ astro(t, 0);
+ return;
+ }
+
+ if(t = alpha(s, "plate")){
+ pplate(t);
+ return;
+ }
+
+ if(t = alpha(s, "gamma")){
+ while(*t==' ')
+ t++;
+ u = t;
+ x = strtod(t, &u);
+ if(u > t)
+ gam.gamma = x;
+ Bprint(&bout, "%.2f\n", gam.gamma);
+ return;
+ }
+
+ if(t = alpha(s, "keep")){
+ if(!cull(t, 1, 0))
+ return;
+ goto Print;
+ }
+
+ if(t = alpha(s, "drop")){
+ if(!cull(t, 0, 0))
+ return;
+ goto Print;
+ }
+
+ for(i=0; planet[i].name[0]; i++){
+ if(t = alpha(s, planet[i].name)){
+ if(doreset)
+ reset();
+ loadplanet(i, nil);
+ goto Print;
+ }
+ }
+
+ for(i=0; names[i].name; i++){
+ if(t = alpha(s, names[i].name)){
+ if(*t){
+ fprint(2, "syntax error in type\n");
+ return;
+ }
+ if(doreset)
+ reset();
+ loadtype(names[i].type);
+ goto Print;
+ }
+ }
+
+ switch(s[0]){
+ case '"':
+ starts = ++s;
+ while(*s != '"')
+ if(*s++ == 0){
+ fprint(2, "bad star name\n");
+ return;
+ }
+ *s = 0;
+ if(doreset)
+ reset();
+ j = nrec;
+ saoopen();
+ starts = fromgreek(starts);
+ for(i=0; i<NName; i++)
+ if(equal(starts, name[i].name)){
+ grow();
+ if(name[i].sao){
+ rec[j].type = NamedSAO;
+ rec[j].index = name[i].sao;
+ }
+ if(name[i].ngc){
+ rec[j].type = NamedNGC;
+ rec[j].index = name[i].ngc;
+ }
+ if(name[i].abell){
+ rec[j].type = NamedAbell;
+ rec[j].index = name[i].abell;
+ }
+ strcpy(rec[j].named.name, name[i].name);
+ j++;
+ }
+ if(parsename(starts))
+ for(i=0; i<NBayer; i++)
+ if(bayer[i].name[0]==parsed[0] &&
+ (bayer[i].name[1]==parsed[1] || parsed[1]==0) &&
+ bayer[i].name[2]==parsed[2]){
+ grow();
+ rec[j].type = NamedSAO;
+ rec[j].index = bayer[i].sao;
+ strncpy(rec[j].named.name, starts, sizeof(rec[j].named.name));
+ j++;
+ }
+ if(j == 0){
+ *s = '"';
+ goto NotFound;
+ }
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ strtoul(s, &t, 10);
+ if(*t != 'h'){
+ BadCoords:
+ fprint(2, "bad coordinates %s\n", inputline);
+ break;
+ }
+ ra = DEG(getra(s));
+ while(*s && *s!=' ' && *s!='\t')
+ s++;
+ rah = ra/15;
+ ra = ra-rah*15;
+ ram = ra*4;
+ deg = strtol(s, &t, 10);
+ if(t == s)
+ goto BadCoords;
+ /* degree sign etc. is optional */
+ chartorune(&c, t);
+ if(c == 0xb0)
+ deg = DEG(getra(s));
+ if(doreset)
+ reset();
+ if(abs(deg)>=90 || rah>=24)
+ goto BadCoords;
+ if(!loadpatch(patch(rah, ram, deg)))
+ goto NotFound;
+ break;
+
+ default:
+ fprint(2, "unknown command %s\n", inputline);
+ return;
+ }
+
+ Print:
+ if(nrec == 0)
+ Bprint(&bout, "empty\n");
+ else if(nrec <= 2)
+ for(i=0; i<nrec; i++)
+ prrec(rec+i);
+ else
+ Bprint(&bout, "%ld items\n", nrec);
+ return;
+
+ NotFound:
+ fprint(2, "%s not found\n", inputline);
+ return;
+}
+
+char *ngctypes[] =
+{
+[Galaxy] "Gx",
+[PlanetaryN] "Pl",
+[OpenCl] "OC",
+[GlobularCl] "Gb",
+[DiffuseN] "Nb",
+[NebularCl] "C+N",
+[Asterism] "Ast",
+[Knot] "Kt",
+[Triple] "***",
+[Double] "D*",
+[Single] "*",
+[Uncertain] "?",
+[Nonexistent] "-",
+[Unknown] " ",
+[PlateDefect] "PD",
+};
+
+char*
+ngcstring(int d)
+{
+ if(d<Galaxy || d>PlateDefect)
+ return "can't happen";
+ return ngctypes[d];
+}
+
+short descindex[NINDEX];
+
+void
+printnames(Record *r)
+{
+ int i, ok, done;
+
+ done = 0;
+ for(i=0; i<NName; i++){ /* stupid linear search! */
+ ok = 0;
+ if(r->type==SAO && r->index==name[i].sao)
+ ok = 1;
+ if(r->type==NGC && r->ngc.ngc==name[i].ngc)
+ ok = 1;
+ if(r->type==Abell && r->abell.abell==name[i].abell)
+ ok = 1;
+ if(ok){
+ if(done++ == 0)
+ Bprint(&bout, "\t");
+ Bprint(&bout, " \"%s\"", togreek(name[i].name));
+ }
+ }
+ if(done)
+ Bprint(&bout, "\n");
+}
+
+int
+equal(char *s1, char *s2)
+{
+ int c;
+
+ while(*s1){
+ if(*s1==' '){
+ while(*s1==' ')
+ s1++;
+ continue;
+ }
+ while(*s2==' ')
+ s2++;
+ c=*s2;
+ if('A'<=*s2 && *s2<='Z')
+ c^=' ';
+ if(*s1!=c)
+ return 0;
+ s1++, s2++;
+ }
+ return 1;
+}
+
+int
+parsename(char *s)
+{
+ char *blank;
+ int i;
+
+ blank = strchr(s, ' ');
+ if(blank==0 || strchr(blank+1, ' ') || strlen(blank+1)!=3)
+ return 0;
+ blank++;
+ parsed[0] = parsed[1] = parsed[2] = 0;
+ if('0'<=s[0] && s[0]<='9'){
+ i = atoi(s);
+ parsed[0] = i;
+ if(i > 100)
+ return 0;
+ }else{
+ for(i=1; i<=24; i++)
+ if(strncmp(greek[i], s, strlen(greek[i]))==0){
+ parsed[0]=100+i;
+ goto out;
+ }
+ return 0;
+ out:
+ if('0'<=s[strlen(greek[i])] && s[strlen(greek[i])]<='9')
+ parsed[1]=s[strlen(greek[i])]-'0';
+ }
+ for(i=1; i<=88; i++)
+ if(strcmp(constel[i], blank)==0){
+ parsed[2] = i;
+ return 1;
+ }
+ return 0;
+}
+
+char*
+dist_grp(int dg)
+{
+ switch(dg){
+ default:
+ return "unknown";
+ case 1:
+ return "13.3-14.0";
+ case 2:
+ return "14.1-14.8";
+ case 3:
+ return "14.9-15.6";
+ case 4:
+ return "15.7-16.4";
+ case 5:
+ return "16.5-17.2";
+ case 6:
+ return "17.3-18.0";
+ case 7:
+ return ">18.0";
+ }
+}
+
+char*
+rich_grp(int dg)
+{
+ switch(dg){
+ default:
+ return "unknown";
+ case 0:
+ return "30-40";
+ case 1:
+ return "50-79";
+ case 2:
+ return "80-129";
+ case 3:
+ return "130-199";
+ case 4:
+ return "200-299";
+ case 5:
+ return ">=300";
+ }
+}
+
+char*
+nameof(Record *r)
+{
+ NGCrec *n;
+ SAOrec *s;
+ Abellrec *a;
+ static char buf[128];
+ int i;
+
+ switch(r->type){
+ default:
+ return nil;
+ case SAO:
+ s = &r->sao;
+ if(s->name[0] == 0)
+ return nil;
+ if(s->name[0] >= 100){
+ i = snprint(buf, sizeof buf, "%C", greeklet[s->name[0]-100]);
+ if(s->name[1])
+ i += snprint(buf+i, sizeof buf-i, "%d", s->name[1]);
+ }else
+ i = snprint(buf, sizeof buf, " %d", s->name[0]);
+ snprint(buf+i, sizeof buf-i, " %s", constel[(uchar)s->name[2]]);
+ break;
+ case NGC:
+ n = &r->ngc;
+ if(n->type >= Uncertain)
+ return nil;
+ if(n->ngc <= NNGC)
+ snprint(buf, sizeof buf, "NGC%4d ", n->ngc);
+ else
+ snprint(buf, sizeof buf, "IC%4d ", n->ngc-NNGC);
+ break;
+ case Abell:
+ a = &r->abell;
+ snprint(buf, sizeof buf, "Abell%4d", a->abell);
+ break;
+ }
+ return buf;
+}
+
+void
+prrec(Record *r)
+{
+ NGCrec *n;
+ SAOrec *s;
+ Abellrec *a;
+ Planetrec *p;
+ int i, rah, ram, dec, nn;
+ long key;
+
+ if(r) switch(r->type){
+ default:
+ fprint(2, "can't prrec type %d\n", r->type);
+ exits("type");
+
+ case Planet:
+ p = &r->planet;
+ Bprint(&bout, "%s", p->name);
+ Bprint(&bout, "\t%s %s",
+ hms(angle(p->ra)),
+ dms(angle(p->dec)));
+ Bprint(&bout, " %3.2f° %3.2f°",
+ p->az/(double)MILLIARCSEC, p->alt/(double)MILLIARCSEC);
+ Bprint(&bout, " %s",
+ ms(angle(p->semidiam)));
+ if(r->index <= 1)
+ Bprint(&bout, " %g", p->phase);
+ Bprint(&bout, "\n");
+ break;
+
+ case NGC:
+ n = &r->ngc;
+ if(n->ngc <= NNGC)
+ Bprint(&bout, "NGC%4d ", n->ngc);
+ else
+ Bprint(&bout, "IC%4d ", n->ngc-NNGC);
+ Bprint(&bout, "%s ", ngcstring(n->type));
+ if(n->mag == UNKNOWNMAG)
+ Bprint(&bout, "----");
+ else
+ Bprint(&bout, "%.1f%c", n->mag/10.0, n->magtype);
+ Bprint(&bout, "\t%s %s\t%c%.1f'\n",
+ hm(angle(n->ra)),
+ dm(angle(n->dec)),
+ n->diamlim,
+ DEG(angle(n->diam))*60.);
+ prdesc(n->desc, desctab, descindex);
+ printnames(r);
+ break;
+
+ case Abell:
+ a = &r->abell;
+ Bprint(&bout, "Abell%4d %.1f %.2f° %dMpc", a->abell, a->mag10/10.0,
+ DEG(angle(a->rad)), a->dist);
+ Bprint(&bout, "\t%s %s\t%.2f %.2f\n",
+ hm(angle(a->ra)),
+ dm(angle(a->dec)),
+ DEG(angle(a->glat)),
+ DEG(angle(a->glong)));
+ Bprint(&bout, "\tdist grp: %s rich grp: %s %d galaxies/°²\n",
+ dist_grp(a->distgrp),
+ rich_grp(a->richgrp),
+ a->pop);
+ printnames(r);
+ break;
+
+ case SAO:
+ s = &r->sao;
+ Bprint(&bout, "SAO%6ld ", r->index);
+ if(s->mag==UNKNOWNMAG)
+ Bprint(&bout, "---");
+ else
+ Bprint(&bout, "%.1f", s->mag/10.0);
+ if(s->mpg==UNKNOWNMAG)
+ Bprint(&bout, ",---");
+ else
+ Bprint(&bout, ",%.1f", s->mpg/10.0);
+ Bprint(&bout, " %s %s %.4fs %.3f\"",
+ hms(angle(s->ra)),
+ dms(angle(s->dec)),
+ DEG(angle(s->dra))*(4*60),
+ DEG(angle(s->ddec))*(60*60));
+ Bprint(&bout, " %.3s %c %.2s %ld %d",
+ s->spec, s->code, s->compid, s->hd, s->hdcode);
+ if(s->name[0])
+ Bprint(&bout, " \"%s\"", nameof(r));
+ Bprint(&bout, "\n");
+ printnames(r);
+ break;
+
+ case Patch:
+ radec(r->index, &rah, &ram, &dec);
+ Bprint(&bout, "%dh%dm %d°", rah, ram, dec);
+ key = r->patch.key[0];
+ Bprint(&bout, " %s", constel[key&0xFF]);
+ if((key>>=8) & 0xFF)
+ Bprint(&bout, " %s", constel[key&0xFF]);
+ if((key>>=8) & 0xFF)
+ Bprint(&bout, " %s", constel[key&0xFF]);
+ if((key>>=8) & 0xFF)
+ Bprint(&bout, " %s", constel[key&0xFF]);
+ for(i=1; i<r->patch.nkey; i++){
+ key = r->patch.key[i];
+ switch(key&0x3F){
+ case SAO:
+ Bprint(&bout, " SAO%ld", (key>>8)&0xFFFFFF);
+ break;
+ case Abell:
+ Bprint(&bout, " Abell%ld", (key>>8)&0xFFFFFF);
+ break;
+ default: /* NGC */
+ nn = (key>>16)&0xFFFF;
+ if(nn > NNGC)
+ Bprint(&bout, " IC%d", nn-NNGC);
+ else
+ Bprint(&bout, " NGC%d", nn);
+ Bprint(&bout, "(%s)", ngcstring(key&0x3F));
+ break;
+ }
+ }
+ Bprint(&bout, "\n");
+ break;
+
+ case NGCN:
+ if(r->index <= NNGC)
+ Bprint(&bout, "NGC%ld\n", r->index);
+ else
+ Bprint(&bout, "IC%ld\n", r->index-NNGC);
+ break;
+
+ case NamedSAO:
+ Bprint(&bout, "SAO%ld \"%s\"\n", r->index, togreek(r->named.name));
+ break;
+
+ case NamedNGC:
+ if(r->index <= NNGC)
+ Bprint(&bout, "NGC%ld \"%s\"\n", r->index, togreek(r->named.name));
+ else
+ Bprint(&bout, "IC%ld \"%s\"\n", r->index-NNGC, togreek(r->named.name));
+ break;
+
+ case NamedAbell:
+ Bprint(&bout, "Abell%ld \"%s\"\n", r->index, togreek(r->named.name));
+ break;
+
+ case PatchC:
+ radec(r->index, &rah, &ram, &dec);
+ Bprint(&bout, "%dh%dm %d\n", rah, ram, dec);
+ break;
+ }
+}