Skip to content
Snippets Groups Projects
partibrains.c 160 KiB
Newer Older
teuben's avatar
teuben committed
		ac -= 2, av += 2;
	    else if(av[0][1] == 't' && (tno = getfloat(av[1], st->datatime)) >= 0)
		ac -= 2, av += 2;
	    else
		break;
	}
	if(ac == 2 &&
	     3==sscanf(av[0], "%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2]) &&
	     0<(k=sscanf(av[1], "%f%*c%f%*c%f", &rad.x[0],&rad.x[1],&rad.x[2]))) {
	    if(k<3) rad.x[1] = rad.x[2] = rad.x[0];	/* if scalar radius */
	    vsub(&box.p0, &cen, &rad);
	    vadd(&box.p1, &cen, &rad);
	    specks_add_box( st, &box, tno );

	} else if(ac == 3 &&
		    2==sscanf(av[0], "%f%*c%f",&box.p0.x[0],&box.p1.x[0]) &&
		    2==sscanf(av[1], "%f%*c%f",&box.p0.x[1],&box.p1.x[1]) &&
		    2==sscanf(av[2], "%f%*c%f",&box.p0.x[2],&box.p1.x[2])) {

	    specks_add_box( st, &box, tno );

	} else if(ac == 6) {
	    for(k=3; --k >= 0; ) {
		if(sscanf(av[k], "%f", &box.p0.x[k])<=0
			|| sscanf(av[k+3], "%f", &box.p1.x[k])<=0)
		    break;
	    }
	    if(k<0)
		specks_add_box( st, &box, tno );
	    else
		msg("box: expected xmin ymin zmin  xmax ymax zmax  -or- xmin,xmax ymin,ymax zmin,zmax -or- xcen,ycen,zcen xrad,yrad,zrad");

	} else if(ac == 1) {
	    char *realfile = findfile( fname, av[0] );
	    if(realfile == NULL) {
		msg("%s: boxes: can't find file %s", fname, av[0]);
	    } else {
		specks_read_boxes( st, realfile, tno );
	    }
	} else {
	    msg("usage: box [-t timestep] AMRboxfile  -or-");
	    msg("  box [-t time] [-n boxno] [-l level] xcen,ycen,zcen  xradius,yradius,zradius  -or-");
	    msg("  box [options]  xmin ymin zmin  xmax ymax zmax");
	    msg(" options: -n <boxno>  box number, for \"gobox\" and \"boxlabel\" cmds (dflt -1)");
	    msg("   -l <boxlevel>      level-number (0..31) for \"showboxlevel\" cmds (dflt 0)");
	    msg("   -t <time>		timestep, if animated; default eternal,");
	    msg("			or (for animated AMRboxfile) start time 0");
	}

	
    } else if(!strncmp(key, "annot", 5)) {
	static char ausage[] = "Usage: annot [-t timestep]  string...";
	char *es, *s = strtok(NULL, "\r\n");
	int tno = st->datatime;

	if(s == NULL) {
	    msg(ausage);
	    continue;
	}
	while(*s && isspace(*s)) s++;
	if(!strncmp(s, "-t", 2)) {
	    if((tno = getfloat(s+2, st->datatime)) < 0) {
		msg(ausage);
		continue;
	    }
	    for(s += 2; isspace(*s) || *s=='+' || *s=='-'; s++) ;
	    while(isdigit(*s)) s++;
	    while(*s && isspace(*s)) s++;
	}
	if(*s == '"') s++;
	specks_add_annotation( st, s, tno );

    } else if(!strcmp(key, "size")) {
	key = strtok(NULL, separ);
	if(key) s.size = atof(key);

    } else if(!strcmp(key, "scale")) {	/* "scale" in data file, not as VIRDIR command */
	float v;
	key = strtok(NULL, separ);
	if(key != NULL && (v = atof(key)) != 0)
	    st->spacescale = speckscale = v;

    } else if(!strcmp(key, "tfm")) {
	int inv = 0;
	int mul = 0;
	int hpr = 0;
	int any, more;
	float scl;
	Matrix ot, t;
	Point xyz;
	float aer[3];
	key = strtok(NULL, "\n");
	if(key == NULL) key = "";
	for(more = 1; *key != '\0' && more; key++) {
	  switch(*key) {
	  case '*': mul = 1; break;
	  case '/': inv = 1; break;
	  case 'h': case 'p': case 'r': hpr = 1; break;
	  case ' ': case '\t': break;
	  default: more = 0; key--; break;
	  }
	}

	parti_geto2w( st, parti_object( NULL, &st ), &ot );

	switch(any = sscanf(key, "%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f",
			&t.m[0],&t.m[1],&t.m[2],&t.m[3],
			&t.m[4],&t.m[5],&t.m[6],&t.m[7],
			&t.m[8],&t.m[9],&t.m[10],&t.m[11],
			&t.m[12],&t.m[13],&t.m[14],&t.m[15])) {
	
	case 1:
		scl = t.m[0];
		t = Tidentity;
		t.m[0*4+0] = t.m[1*4+1] = t.m[2*4+2] = scl;
		break;
	case 6:
	case 7:		/* ignore fovy if included */
		xyz = *(Point *)&t.m[0];
		if(hpr) {
		    aer[0] = t.m[3], aer[1] = t.m[4], aer[2] = t.m[5];
		} else {
		    /* Note we permute: px py pz rx ry rz == px py pz e a r */
		    aer[1] = t.m[3], aer[0] = t.m[4], aer[2] = t.m[5];
		}
		xyzaer2tfm( &t, &xyz, aer );
		break;

	case 16: break;
	case 0:  t = ot; break;
	default:
	    msg("Usage: tfm: expected 16 numbers");
	    continue;
	}
	if(any) {
	    if(inv)
		eucinv( &t, &t );
	    if(mul)
		mmmul( &t, &ot, &t );
	    if(strchr(key, '*'))
		mmmul( &t, &t, &ot );

	    /* with trailing '=', just print result without assignment */

	    if(NULL==strchr(key, '='))
		parti_seto2w( st, parti_object( NULL, &st ), &t );
	}

	tfm2xyzaer( &xyz, aer, &t );
	msg("obj2w:  x y z rx ry rz   %g %g %g  %g %g %g",
		xyz.x[0],xyz.x[1],xyz.x[2],  aer[1],aer[0],aer[2] );
	for(i = 0; i < 3; i++)
	    msg(" %10.7f %10.7f %10.7f %10.7f", t.m[i*4+0],t.m[i*4+1],t.m[i*4+2],t.m[i*4+3]);
	msg(" %10.7g %10.7g %10.7g %10.7g", t.m[3*4+0],t.m[3*4+1],t.m[3*4+2],t.m[3*4+3]);
	eucinv( &t, &t );
	tfm2xyzaer( &xyz, aer, &t );
	msg("w2obj:  x y z rx ry rz   %g %g %g  %g %g %g",
		xyz.x[0],xyz.x[1],xyz.x[2],  aer[1],aer[0],aer[2] );
	for(i = 0; i < 3; i++)
	    msg(" %10.7f %10.7f %10.7f %10.7f", t.m[i*4+0],t.m[i*4+1],t.m[i*4+2],t.m[i*4+3]);
	msg(" %10.7g %10.7g %10.7g %10.7g", t.m[3*4+0],t.m[3*4+1],t.m[3*4+2],t.m[3*4+3]);


    } else if(!strcmp(key, "eval") || !strcmp(key, "feed")
				   || !strcasecmp(key, "VIRDIR")) {
	char *av[128];
	int ac;
	char *cmd = strtok(NULL, "\n");
	char *savedcmd = NewA( char, strlen(cmd) + 1 );

	strcpy(savedcmd, cmd);
	key = strtok(cmd, separ);
	for(ac = 0; ac < 127 && key != NULL; ac++) {
	    av[ac] = key;
	    key = strtok(NULL, separ);
	}
	av[ac] = NULL;
#if CAVE
	if(0 == specks_parse_args( &st, ac, av )) 
	    VIDI_queue_commandstr( savedcmd );

#else /* non-CAVE */
	specks_parse_args( &st, ac, av );

#endif /* non-CAVE */

    } else if(!strcmp(key, "ignorefirst") || !strcmp(key, "ignorepgc")) {
	ignorefirst = 1;

    } else if(!strcmp(key, "filepath")) {
	int m, k = 0;
	char **oldpath = getfiledirs();
	char *path[64];
	while(k < 64-1 && (key = strtok(NULL, ": \t\r\n")) != NULL) {
	    if(!strcmp(key, "+")) {
		for(m = 0; k<64-1&&oldpath!=NULL&&oldpath[m]!=NULL; m++)
		    path[k++] = strdup(oldpath[m]);
	    } else {
		path[k++] = strdup(key);
	    }
	}
	path[k] = NULL;
	if(k > 0) filedirs(path);
	while(--k >= 0) free(path[k]);

    } else if(!strcmp(key, "texture")) {
slevy's avatar
 
slevy committed
	int txno = 0;
teuben's avatar
teuben committed
	int txflags = TXF_SCLAMP | TXF_TCLAMP;
	int txapply = TXF_DECAL;
	int qual = 7;
	char *txfname;
	key = strtok(NULL, separ);
slevy's avatar
 
slevy committed
	sscanf(key, "%d", &txno);
	while(key != NULL && (key[0] == '-' || key[0] == '+')) {
	    int tqual = (strchr(key,'m') ? 4:0) |
			(strchr(key,'l') ? 2:0) |
			(strchr(key,'n') ? 1:0);
	    if(key[0] == '-') qual &= ~tqual;
	    else qual |= tqual;
	    if(strchr(key,'a')) txflags |= TXF_ALPHA;
	    if(strchr(key,'i')) txflags |= TXF_INTENSITY;
	    if(strchr(key, 'A')) txflags |= TXF_ADD;
teuben's avatar
teuben committed
	    if(strchr(key, 'M')) txapply = TXF_MODULATE;
	    if(strchr(key, 'D')) txapply = TXF_DECAL;
	    if(strchr(key, 'B')) txapply = TXF_BLEND;
	    key = strtok(NULL, separ);
	}
slevy's avatar
 
slevy committed
	if(key == NULL || (txno == 0 && sscanf(key, "%d", &txno) <= 0)
teuben's avatar
teuben committed
	   || (txfname = strtok(NULL, separ)) == NULL) {
slevy's avatar
 
slevy committed
		msg("Expected ``texture [-lmnaMDB] txno file.sgi'', got %s", oline);
		msg(" opts: -l(inear) -m(ipmap) -n(earest) -i(intensity) -a(lpha) -A(dd) -M(odulate)|-D(ecal)|-B(lend))");
teuben's avatar
teuben committed
	} else {
	    txaddentry( &st->textures, &st->ntextures, fname, txno, txfname, txapply, txflags, qual );
	}

    } else if(!strcmp(key, "polyorivar")) {
	if((key = strtok(NULL, separ)) != NULL)
	    if(1!=specks_set_byvariable( st, key, &st->polyorivar0 ))
		msg("polyorivar: unknown field %s", key);
teuben's avatar
teuben committed

    } else if(!strcmp(key, "texturevar")) {
	if((key = strtok(NULL, separ)) != NULL)
	    if(1!=specks_set_byvariable( st, key, &st->texturevar ))
		msg("texturevar: unknown field %s", key);
teuben's avatar
teuben committed

    } else if(!strncmp(key, "coord", 5) || !strncmp(key, "altcoord", 8)) {
	float *t = &st->altcoord[0].w2coord.m[0];
	key = strtok(NULL, separ);
	if(key == NULL) {
	    msg("coord %s  %g %g %g %g  %g %g %g %g  %g %g %g %g  %g %g %g %g",
		st->altcoord[0].name, t[0],t[1],t[2],t[3],
		t[4],t[5],t[6],t[7],
		t[8],t[9],t[10],t[11],
		t[12],t[13],t[14],t[15]);
	} else {
	    sprintf(st->altcoord[0].name, "%.9s", key);
	    key = strtok(NULL, "\n");
	    if(key != NULL && 16 != sscanf(key, "%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f",
			t,t+1,t+2,t+3, t+4,t+5,t+6,t+7, t+8,t+9,t+10,t+11,
			t+12,t+13,t+14,t+15)) {
		key = NULL;
	    }
	    if(key == NULL) {
		msg("expected \"coord\" name  ... 16 world-to-coord tfm floats (GL order) ...");
	    }
	}
    } else if(!strncmp(key, "dataset", 7)) {
	char name[12];
	int indexno;
	if((key = strtok(NULL,"\n")) == NULL ||
		sscanf(key, "%d %11s", &indexno, name) < 2
		|| indexno < 0 || indexno >= MAXFILES) {
	    msg("%s: expected ``dataset <indexno> <datasetname>'' with 0<=indexno<=%d",
		fname, MAXFILES-1);
	    continue;
	}
	strncpyt(st->dataname[indexno], name, sizeof(st->dataname[indexno]));
	st->curdata = indexno;

    } else if(!strncmp(key, "datavar", 7)) {
	int varno, nargs;
	struct valdesc vd;

	if((key = strtok(NULL, "\n")) == NULL ||
		(nargs = sscanf(key, "%d %19s %f %f", &varno, vd.name, &vd.min, &vd.max)) < 2
		|| varno < 0 || varno >= MAXVAL) {
	    msg("%s: expected ``datavar <indexno> <variablename>'' with 0<=indexno<=%d",
		fname, MAXVAL-1);
	    continue;
	}
	strcpy(st->vdesc[st->curdata][varno].name, vd.name);
	if(nargs == 4) {
	    st->vdesc[st->curdata][varno].min = vd.min;
	    st->vdesc[st->curdata][varno].max = vd.max;
	}

    } else if(!strcmp(key, "datatime")) {
	int newt = st->curtime;	/* so e.g. "datatime now" => apply to current data */
	if((key = strtok(NULL, separ)) == NULL) {
	    msg("%s: datatime %d", fname, st->datatime);
	    continue;
	}
	sscanf(key, "%d", &newt);
	if(newt < 0) {
	    msg("%s: datatime %s: timestep must be >= 0", fname, key);
	    continue;
	}
	if(newt == st->datatime)	/* no need to change anything */
	    continue;
	if(nsp > 0) {			/* else flush accumulated specks */
	    addchunk( st, nsp, sizeof(struct speck), 
		speckscale, sp, NULL,
		maxfields > MAXVAL ? sizeof(struct speck)
				   : SMALLSPECKSIZE( maxfields ) );
	    nsp = maxfields = 0;
	}
	st->datatime = newt;

    } else if(!strcmp(key, "ellipsoid")) {

    } else if(!strcmp(key, "mesh") || !strcmp(key, "tstrip") || !strcmp(key, "tfan")) {
	specks_read_mesh(st, key, f, line);
	
	
    } else if(!strcmp(key, "textcolor")) {
	key = strtok(NULL, separ);
	if(key)
	    sscanf(key, "%d", &s.rgba);

teuben's avatar
teuben committed
    } else {
	struct valdesc *vdp;

	i = 0;
	if(!ignorefirst)
	    s.p.x[i++] = atof(key) * speckscale;
	for( ; i < 3 && (key = strtok(NULL,separ)) != NULL; i++) {
	    s.p.x[i] = atof(key) * speckscale;
	}
	if(i < 3)
	    continue;
	vdp = &st->vdesc[st->curdata][0];
	for(i = 0; (key = strtok(NULL,separ)) != NULL; i++) {
	    if(key[0] == 't') break;	/* "text" */
	    if(key[0] == '#') break;
	    if(i < COUNT(s.val)) {
		s.val[i] = atof(key);
		if(vdp->nsamples++ == 0) {
		    vdp->min = vdp->max = s.val[i];
		} else {
		    if(vdp->min > s.val[i]) vdp->min = s.val[i];
		    else if(vdp->max < s.val[i]) vdp->max = s.val[i];
		}
		vdp->sum += s.val[i];
		vdp->nsamples++;
		vdp->mean = vdp->sum / vdp->nsamples;
		vdp++;
	    }
	}
	if(maxfields < i) maxfields = i;
	s.title[0] = '\0';
	if(key && key[0] == '#') {
	    key = strtok(NULL, "\r\n");
	    if(key) {
		while(isspace(key[0])) key++;
		for(i = strlen(key); --i >= 0 && isspace(key[i]); )
		    key[i] = '\0';
		sprintf(s.title, "%.*s", (int)(sizeof(s.title)-1), key);
		maxfields = MAXVAL+1;	/* "keep title too" */
	    }
	}

	sp[nsp++] = s;

	if(key != NULL && key[0] == 't') {	/* "text" */
	    s.size = 1;
	    key = strtok(NULL, "\n");
	    if(key && !strncmp(key, "-size", 5)) {
		s.size = strtod( key+5, &key );
		if(*key) key++;
		if(s.size == 0) s.size = 1;
	    }
	    addchunk( st, 1, SMALLSPECKSIZE(0), speckscale, &s, key,
			     SMALLSPECKSIZE(0) );
	    nsp--;
	}
    }
  }
  fclose(f);
  SPFLUSH();
  *stp = st;
}

/* Add a static (eternal) box to display list */
int specks_add_box( struct stuff *st, struct AMRbox *box, int timestep )
{
  int i;
  if(box->level >= 0 && st->boxlevels <= box->level)
    st->boxlevels = box->level+1;
  for(i = 0; i < st->staticboxroom && st->staticboxes[i].level >= 0; i++) {
    if(box->boxno != -1 && box->boxno == st->staticboxes[i].boxno) {
	st->staticboxes[i] = *box;
	return ~i;
    }
  }

  if(i >= st->staticboxroom-1) {
    st->staticboxroom = (st->staticboxroom>0) ? st->staticboxroom*2 : 12;
    st->staticboxes = RenewN( st->staticboxes, struct AMRbox, st->staticboxroom );
  }
  st->staticboxes[i] = *box;
  st->staticboxes[i+1].level = -1;
  return i;
}


void specks_read_boxes( struct stuff *st, char *fname, int timebase )
{
  FILE *f = fopen(fname, "r");
  char line[1024];
  int ntimes = -1, maxbox = -1, curbox = 0, curtime = 0, level = -1, i;
  int lno = 0;
  int boxseq, boxno;
  struct AMRbox *boxes = NULL;
  struct AMRbox box;

  if(timebase < 0)	/* if unspecified time, start at first timestep */
    timebase = 0;

  if(f == NULL) {
    msg("Can't open AMRboxes file %s (as from hier2boxes.pl)", fname);
    return;
  }
  while(fgets(line, sizeof(line), f) != NULL) {
    lno++;
    boxno = -1;
    if(sscanf(line, "%f%f%f %f%f%f # %d",
		&box.p0.x[0],&box.p0.x[1],&box.p0.x[2],
		&box.p1.x[0],&box.p1.x[1],&box.p1.x[2], &boxno) >= 6) {
	for(i = 0; i < 3; i++) {
	    box.p0.x[i] *= st->spacescale;
	    box.p1.x[i] *= st->spacescale;
	}
	if(boxno != -1)
	    box.boxno = boxno;
	else
	    box.boxno = boxseq++;
	if(curbox < maxbox && boxes != NULL && level >= 0) {
	    boxes[curbox] = box;
	    curbox++;
	} else {
	    msg("%s line %d: Excess box (only expected %d at timestep %d)",
		fname, lno, maxbox, curtime);
	}
    }
    else if(sscanf(line, "AMRboxes %d timesteps", &ntimes) > 0 && ntimes >= 0) {
	int needtimes = ntimes + (timebase<0 ? 0 : timebase);
	if(st->boxtimes <= needtimes+1) {
	    needtimes += st->boxtimes + 15;	/* leave lots of extra room */
	    st->boxes = RenewN( st->boxes, struct AMRbox *, needtimes );
	    memset( &st->boxes[st->boxtimes], 0,
			(needtimes - st->boxtimes) * sizeof(struct AMRbox *) );
	    if(st->boxtimes < ntimes+timebase)
		st->boxtimes = ntimes+timebase;
	}
	boxes = NULL;
    }
    else if(sscanf(line, "timestep %d %d grids", &curtime, &maxbox) == 2) {
	boxseq = 1;
	if(ntimes < 0) {
	    msg(
"%s line %d: ``AMRboxes N timesteps'' must precede ``timestep'' lines!",
		    fname, lno);
	    curtime = -1;
	    boxes = NULL;
	    continue;
	} else if(curtime<0 || curtime>=ntimes || maxbox<0) {
	    msg("%s line %d: bad timestep number", fname, lno); 
	    curtime = -1;
	    boxes = NULL;
	    continue;
	}
	if((boxes = st->boxes[curtime+timebase]) != NULL) {
	    msg("%s line %d: Respecifying timestep %d",
		fname, lno, curtime);
	    Free( st->boxes[curtime+timebase] );
	}
	boxes = NewN( struct AMRbox, maxbox+1 );
	for(curbox = 0; curbox <= maxbox; curbox++)
	    boxes[curbox].level = -(maxbox+1);
	st->boxes[curtime+timebase] = boxes;

	/* Ensure that known span of time includes this timestep */
	(void) specks_timespecksptr( st, st->curdata, curtime+timebase );

	curbox = 0;
    }
    else if(sscanf(line, "level %d", &level) > 0) {
	if(boxes == NULL) {
	    msg("%s line %d: Must specify timestep before level",
		fname, lno);
	    level = -1;
	}
	if(st->boxlevels <= level)
	    st->boxlevels = level+1;
	box.level = level;
    }
  }
  fclose(f);

  /* st->boxlevelmask |= (1 << st->boxlevels) - 1;
   * Don't do this -- it turns on all boxlevels whenever we get a new box!
   */
}

static struct AMRbox *findboxno( struct AMRbox *box, int boxno, int *count, int *bmin, int *bmax ) {
  if(box == NULL) return NULL;
  while(box->level >= 0) {
    if(box->boxno == boxno)
	return box;
    if(*bmin > box->boxno) *bmin = box->boxno;
    if(*bmax < box->boxno) *bmax = box->boxno;
    ++*count;
    box++;
  } 
  return NULL;
}

int specks_gobox( struct stuff *st, int boxno, int argc, char *argv[] )
{
  Point pmid;
  float sz, scale;
  char cmd[128];
  int bmin = 1<<30, bmax = -1<<30;
  int count = 0;
  struct AMRbox *box = NULL;

  if(st->boxes && st->curtime < st->boxtimes)
    box = findboxno( st->boxes[st->curtime], boxno, &count, &bmin, &bmax );
  if(box == NULL)
    box = findboxno( st->staticboxes, boxno, &count, &bmin, &bmax );

  if(box == NULL) {
    if(count == 0)
	msg("No AMR boxes for timestep %d", st->curtime);
    else
	msg("%d AMR boxes (numbered %d..%d) for timestep %d, but no box number %d",
	    count, bmin, bmax, st->curtime, boxno);
    return 0;
  }

  vlerp( &pmid, .5*st->gscale, &box->p0, &box->p1 );
  set_interest_point( &pmid );

  sz = vdist( &box->p0, &box->p1 );
  sprintf(cmd, "%.1g", st->gscale * st->goboxscale * sz);
		/* round to 1 decimal */
  scale = atof(cmd);
#if CAVE
  {
    Point pmid, fwd, headv, pos, offset, newpos;
    static Point zvec = {0,0,1};
    CAVENavConvertVectorCAVEToWorld( zvec.x, fwd.x );
    CAVEGetPosition( CAVE_HEAD, headv.x );
    headv.x[2] -= 10.0;	/* a point in front of head, in CAVE coords */
    CAVENavConvertVectorCAVEToWorld( headv.x, offset.x );
    msg("scale %g caveunit %g offsetW %g %g %g", scale, vlength(&fwd), offset.x[0],offset.x[1],offset.x[2]);
    vsadd( &newpos, &pmid, -scale / vlength(&fwd), &offset );
    sprintf(cmd,
	sz > 0	? "jumpto %g %g %g  . . .  %g"
		: "jumpto %g %g %g",
	newpos.x[0], newpos.x[1], newpos.x[2], scale);
    VIDI_queue_commandstr( cmd );
  }
#endif

  return 1;
}

void specks_read_cmap( struct stuff *st, char *fname, int *ncmapp, struct cment **cmapp )
teuben's avatar
teuben committed
{
  int i;
  char *cp;
  char line[256];
  int k, count = 0;
  int *tcmap;
teuben's avatar
teuben committed
  float fr,fg,fb,fa;
  int big = 0;
  int lno = 0;
  int ncmap = 0;
  int cval;
  int csrc;

  FILE *f = fopen(fname, "r");
  if(f == NULL) {
    msg("Can't open colormap file %s", fname);
    return;
  }
  tcmap = NULL;

  ncmap = 0;
  while(fgets(line, sizeof(line), f) != NULL) {
    lno++;
    cp = line;
   rescan:
    for( ; isspace(*cp); cp++)
	;
    if(*cp == '\0' || *cp == '#')
	continue;
    fa = 1.0;
    k = sscanf(cp, "%f%f%f", &fr,&fg,&fb/*,&fa*/);
    if(k == 1) {
	if(count == 0 && fr == (int)fr && fr > 0) {
	    count = fr;
	    if(count >= 1 && count < 1000000)		/* OK */
		continue;
	    msg("Unreasonable number of colormap entries claimed in header");
	    /* and fall into error case */
	} else {
	    while(isspace(*cp) || isdigit(*cp)) cp++;
	    if(*cp == ':') {
		ncmap = fr;
		if(ncmap < 0) {
		    ncmap = 0;		/* don't continue -- fall into error */
		} else if(ncmap >= count) {
		    msg("Colormap index %g exceeds size %d given in header",
			fr, count);	/* don't continue -- ditto */
		} else {
		    cp++;
		    goto rescan;		/* All's well */
		}
	    }
	}
    } else if(k >= 3) {
	if(fr > 2 || fg > 2 || fb > 2 || fa > 2)
	    big = 1;
	if(!big) {
	    fr *= 255; fg *= 255; fb *= 255; /*fa *= 255;*/
	}
	cval = PACKRGBA( (int)fr, (int)fg, (int)fb, 0 );
	if(tcmap == NULL) {
	    tcmap = NewA( int, count );
	    /* Fill unused entries with gray */
	    memset(tcmap, 0x80, count*sizeof(int));
	}
	tcmap[ncmap>=count ? count-1 : ncmap<0 ? 0 : ncmap] = cval;
	ncmap++;
	if(ncmap >= count)
	    break;
	continue;

    } else if(sscanf(cp, "=%d", &csrc) > 0) {
	if(csrc < 0 || csrc >= count || ncmap < 0 || ncmap >= count || tcmap == NULL) {
	    msg("Colormap index out of range: %d or %d isn't in 0..%d",
		ncmap, csrc, count-1);
	} else {
	    tcmap[ncmap++] = tcmap[csrc];
	    continue;
	}
	/* Fall through into error */
    }
    msg("Trouble reading colormap file %s: bad line %d: %s",
		fname, lno, line);
    fclose(f);
    return;
  }
  fclose(f);
  if(tcmap == NULL)
    return;

  *ncmapp = count;
  if(*ncmapp < 2) {
    *ncmapp = 2;
    tcmap[1] = tcmap[0];
  }
  *cmapp = cm = RenewN( *cmapp, struct cment, *ncmapp );
  k = *ncmapp;
  for(i = 0; i < k; i++) {
    cm[i].raw = tcmap[i];
    cm[i].cooked = specks_cookcment( st, tcmap[i] );
slevy's avatar
 
slevy committed
  }
teuben's avatar
teuben committed
}


#if CAVEMENU
void set_psize( float psize, MenuEnt *me, void *st ) {
   char str[20];
   ((struct stuff *)st)->psize = psize;
   sprintf(str, "pointsize %.3g", psize);
   menu_settitle( me, str );
}

	/* set_tknob() only used if PARTIMENU mentions "survey" */
void set_tknob( float val, MenuEnt *me, void *vtb ) {
  char str[128];
  struct stuff *st = ((struct boxleveler *)vtb)->st;
  int self = ((struct boxleveler *)vtb)->level;
  static int mod[3] = { 1, 4, 16 };
  static char *fmt[3] = {
	"StarMassEjFrac [%d/4]",
	"SNFeedback     [%d/4]",
	"StarEfficiency [%d/4]",
  };
  int range = 4, mine, rest, above;
  above = mod[self] * range;
  rest = st->curtime % mod[self] + (st->curtime / above) * above;
  mine = (st->curtime / mod[self]) % range;
  if(me->state == ME_PICKED || me->state == ME_HELD) {
    mine = val;
    if(mine < 0) mine = 0; else if(mine >= range) mine = range-1;
slevy's avatar
 
slevy committed
    clock_set_time( st->clk, rest + mine*mod[self] );
    clock_set_running( st->clk, 0 );
teuben's avatar
teuben committed
  }
  sprintf(str, fmt[self], mine+1);
  menu_settitle( me, str );
}

void set_step( float time0, MenuEnt *me, void *vst ) {
   char str[32];
   struct stuff *st = (struct stuff *)vst;
   if(me->state == ME_PICKED) {
slevy's avatar
 
slevy committed
	time0 += clock_fwd(st->clk);
teuben's avatar
teuben committed
	st->timeplay = 0;
	me->state = ME_HELD;
   }
slevy's avatar
 
slevy committed
   clock_set_time( st->clk, me->val = (int) time0 );
teuben's avatar
teuben committed
   /* NOTE we use the static values snapped by the framefunction.
    * st->curtime etc. might have changed since then.
    */
   sprintf(str, "step %2d of 0..%d", st->frame_time, st->ntimes - 1);
   if(st->fetching > 0)
	sprintf(str+strlen(str), " (loading %d)", st->fetchtime);
   menu_settitle( me, str );
}

void set_fwd( int fwd, MenuEnt *me, void *st ) {
slevy's avatar
 
slevy committed
   clock_set_fwd( ((struct stuff *)st)->clk, fwd );
teuben's avatar
teuben committed
   menu_settitle( me, fwd ? ">>> fwd >>>" : "<<< rev <<<" );
}

void set_viewall( int all, MenuEnt *me, void *vst ) {
  struct stuff *st = (struct stuff *)vst;
  if(all) {
    strcpy( st->vdcmd, "\002jump all" );
    menu_settitle( me, "view all" );
  }
  else {
    strcpy( st->vdcmd, "\002jump peak" );
    menu_settitle( me, "view peak" );
  }
}

float get_nav_scale(void)
{
  float nav[4][4];
  CAVENavGetMatrix( nav );
  return 1 / sqrtf( vdot( (Point *)&nav[0][0], (Point *)&nav[0][0] ) );
}

void set_scale( float logscale, MenuEnt *me, void *vst ) {
  struct stuff *st = (struct stuff *)vst;
  float scl = pow(10., logscale);
  char str[64];

  if(me->state == ME_PICKED || me->state == ME_HELD) {
    sprintf( st->vdcmd, "\002scale %g", scl);
  } else {
    scl = get_nav_scale();
    me->val = scl<=0 ? -6 : log10( scl );
  }
  sprintf( str, "scale %.4g", scl );
  menu_settitle( me, str );
}


void set_dataset( int which, MenuEnt *me, void *vst ) {
   char str[32];
   struct stuff *st = (struct stuff *)vst;
   if(which < 0 || which >= st->ndata || which >= MAXFILES) which = 0;
   st->curdata = which;
   sprintf(str, "viewing %s (%d)", st->dataname[which], which);
   menu_settitle( me, str );
}

void set_lumvar( int lumvar, MenuEnt *me, void *vst ) {
   char str[80];
   struct stuff *st = (struct stuff *)vst;
   struct valdesc *vd;
   if(lumvar < 0 || lumvar >= MAXVAL+1) lumvar = 0;
   st->sizedby = lumvar;
   vd = &st->vdesc[st->curdata][lumvar];
   if(vd->lmin == vd->lmax) vd->lmin = vd->min, vd->lmax = vd->max;
   sprintf(str, "lum by %s (%d) [%.3g..%.3g]", vd->name, lumvar, vd->lmin, vd->lmax);
   menu_settitle( me, str );
}

void set_colorvar( int colorvar, MenuEnt *me, void *vst ) {
   char str[80];
   struct stuff *st = (struct stuff *)vst;
   struct valdesc *vd;
   if(colorvar < 0 || colorvar >= MAXVAL+1) colorvar = 0;
   st->coloredby = colorvar;
   vd = &st->vdesc[st->curdata][colorvar];
   if(vd->cmin == vd->cmax) vd->cmin = vd->min, vd->cmax = vd->max;
   sprintf(str, "color by %s (%d) [%.3g..%.3g]", vd->name,
		colorvar, vd->cmin, vd->cmax);
   menu_settitle( me, str );
}

void set_slum( float slum, MenuEnt *me, void *vst ) {
   char str[32];
   struct stuff *st = (struct stuff *)vst;
   st->vdesc[st->curdata][st->sizedby].lum = slum;
   sprintf(str, slum == 0 ? "scale-lum <unset>" : "scale-lum %.2g", slum);
   menu_settitle( me, str );
}

void set_speed( float speed, MenuEnt *me, void *vst ) {
   char str[32];
   struct stuff *st = (struct stuff *)vst;
teuben's avatar
teuben committed
   specks_set_speed( st, speed );
teuben's avatar
teuben committed
   sprintf(str, "speed %.1f steps/anim sec", speed);
   menu_settitle( me, str );
}

void set_fspeed( float speed, MenuEnt *me, void *vst ) {
   char str[64];
   struct stuff *st = (struct stuff *)vst;
teuben's avatar
teuben committed
   specks_set_fspeed( st, speed );
teuben's avatar
teuben committed
   if(me->state == ME_PICKED) {
slevy's avatar
 
slevy committed
	clock_set_running( st->clk, !clock_running(st->clk) );
teuben's avatar
teuben committed
	me->state = ME_HELD;	/* only once */
   }
slevy's avatar
 
slevy committed
   sprintf(str, "fspeed %.1f steps/real sec (%s)", speed,
			clock_running(st->clk) ? "running" : "stopped");
teuben's avatar
teuben committed
   menu_settitle( me, str );
}

void set_seedata( int which, MenuEnt *me, void *vst ) {
   struct stuff *st = (struct stuff *)vst;
   char str[64];
   if(which >= st->ndata) which = st->ndata - 1;
   if(which < 0) which = 0;
   st->curdata = which;
   sprintf(str, "see %.24s (%d)", st->dataname[which], which);
   menu_settitle( me, str );
}

void set_every( float every, MenuEnt *me, void *vst ) {
   char str[32];
   struct stuff *st = (struct stuff *)vst;
   if(every < 1) every = 1;
   st->subsample = me->val = every;
   sprintf(str, "every %dth p'cle (of %d)", (int)every, specks_count(st->sl));
   menu_settitle( me, str );
}

void set_menu( int on, MenuEnt *me, void *vst ) {
   struct stuff *st = (struct stuff *)vst;
slevy's avatar
 
slevy committed
   int empty = st->ntimes == 0 && st->sl == NULL;
teuben's avatar
teuben committed
   if(me->state == ME_PICKED)
	st->hidemenu = !st->hidemenu;
slevy's avatar
 
slevy committed
   menu_sethidden( pmenu, st->hidemenu || empty );
   menu_sethidden( stubmenu, st->hidemenu<=0 || empty );
teuben's avatar
teuben committed
}

void set_allboxes( int on, MenuEnt *me, void *vst ) {
   struct stuff *st = (struct stuff *)vst;
   st->useboxes = on;
   menu_settitle( me, on==2 ? "Boxes ONLY" :
			on ? "Boxes ON" : "Boxes off" );
}

void set_boxlevel( int on, MenuEnt *me, void *vblr ) {
   struct boxleveler *blr = (struct boxleveler *)vblr;
   struct stuff *st = blr->st;
   int bit = 1 << blr->level;
   char str[32];

   if(blr->level >= st->boxlevels) {
	menu_settitle(me, "" );
   } else {
	if(me->state == ME_PICKED)
	    st->boxlevelmask ^= bit;
	sprintf(str, "Level %d %s", blr->level, 
		st->boxlevelmask & bit ? "ON" : "off" );
	menu_settitle( me, str );
   }
}

void
set_point( int on, MenuEnt *me, void *st )
{
  struct stuff *stuff = (struct stuff *)st;
  stuff->usepoint = (on<0) ? !stuff->usepoint : on;
  menu_settitle( me, stuff->usepoint ? "Point On" : "Point Off" );
}

void
set_poly( int on, MenuEnt *me, void *st )
{
  struct stuff *stuff = (struct stuff *)st;
  stuff->usepoly = (on<0) ? !stuff->usepoly : on;
  menu_settitle( me, stuff->usepoly ? "Poly On" : "Poly Off" );
}

void
set_label( int on, MenuEnt *me, void *st )
{
  struct stuff *stuff = (struct stuff *)st;
  stuff->usetext = (on<0) ? !stuff->usetext : on;
  menu_settitle( me, stuff->usetext ? "Labels On" : "Labels Off" );
}

void
set_alpha( float alpha, MenuEnt *me, void *st )
{
   char str[20];
   ((struct stuff *)st)->alpha = alpha;
   sprintf(str, "Alpha %.2f", alpha);
   menu_settitle( me, str );
}

void
set_fog( float fog, MenuEnt *me, void *st )
{
   char str[20];
   ((struct stuff *)st)->fog = fog;
   sprintf(str, "Fog %.2f (not yet)", fog);
   menu_settitle( me, str );
}

void
set_polysize( float polysize, MenuEnt *me, void *st )
{
   char str[20];
   ((struct stuff *)st)->polysize = polysize;
   sprintf(str, "Spotsize %.2f", polysize);
   menu_settitle( me, str );
}
#endif /*CAVEMENU*/



int specks_set_byvariable( struct stuff *st, char *str, int *val )
{
  int i;
  char *ep;
  int best = -1;
  if(str == NULL) return 0;

  if(!strcasecmp( str, "const" ) || !strcasecmp( str, "constant" )
				 || !strcasecmp( str, "rgb" )) {
    *val = CONSTVAL;
    return -1;
  }
	
  for(i = 0; i < MAXVAL; i++) {
    if(strncasecmp( str, st->vdesc[st->curdata][i].name, strlen(str) ) == 0) {
	best = i;
	if(!strcmp( str, st->vdesc[st->curdata][i].name ))
	    break;
    }
  }
  if(best >= 0) {
    *val = best;
    return 1;
  }
  i = strtol(str, &ep, 0);
  if(ep == str || i < 0 || i > MAXVAL || (*ep != '\0' && *ep != '('))
    return 0;
  *val = i;
  return 1;
}

static char *putcoords( char *buf, Point *pos, Matrix *T,
		int isvec, char *cartfmt,
		char *lonlatfmt, char *hmdmfmt )
{
  Point p;
  char *cp = buf;
  float lat, lon, r;
  if(T == NULL) T = &Tidentity;
  buf[0] = '\0';
  if(isvec)
    vtfmvector( &p, pos, T );
  else vtfmpoint( &p, pos, T );
  if(cartfmt)
    cp += sprintf(cp, cartfmt, p.x[0],p.x[1],p.x[2]);
  lon = atan2(p.x[1], p.x[0]) * 180/M_PI;
  if(lon < 0) lon += 360;
  lat = atan2(p.x[2], hypot(p.x[1],p.x[0])) * 180/M_PI;
  r = vlength(&p);
  if(lonlatfmt)
    cp += sprintf(cp, lonlatfmt, lon, lat, r);
  if(hmdmfmt) {
    char *sign = (lat < 0) ? "-" : "+";
    lat = fabs(lat);
    lon /= 15;
    cp += sprintf(cp, hmdmfmt, (int)lon, 60. * (lon - (int)lon),