Skip to content
Snippets Groups Projects
partibrains.c 205 KiB
Newer Older
teuben's avatar
teuben committed
	    continue;
	}
	strncpyt(st->dataname[indexno], name, sizeof(st->dataname[indexno]));
	st->curdata = indexno;

    } else if(!strncmp(argv[0], "datavar", 7) && argc > 2) {
teuben's avatar
teuben committed
	struct valdesc vd;

	if(sscanf(argv[1], "%d", &varno)<=0
		|| sscanf(argv[2], "%19s", vd.name)<=0
teuben's avatar
teuben committed
		|| varno < 0 || varno >= MAXVAL) {
	    msg("%s: expected ``datavar <indexno> <variablename> [minval maxval]'' with 0<=indexno<=%d",
		line, MAXVAL-1);
teuben's avatar
teuben committed
	    continue;
	}
	strcpy(st->vdesc[st->curdata][varno].name, vd.name);
	if(argc == 5) {
	    st->vdesc[st->curdata][varno].min = atof(argv[3]);
	    st->vdesc[st->curdata][varno].max = atof(argv[4]);
teuben's avatar
teuben committed
	}

    } else if(!strcmp(argv[0], "datatime") && argc==2) {
teuben's avatar
teuben committed
	int newt = st->curtime;	/* so e.g. "datatime now" => apply to current data */
	sscanf(argv[1], "%d", &newt);
	if(sscanf(argv[1], "%d", &newt) <= 0 || newt < 0) {
	    msg("%s: datatime %s: timestep must be >= 0", fname, argv[1]);
teuben's avatar
teuben committed
	    continue;
	}
	if(newt == st->datatime)	/* no need to change anything */
	    continue;
teuben's avatar
teuben committed
	st->datatime = newt;

    } else if(!strcmp(argv[0], "mesh") || !strcmp(argv[0], "tstrip")
					|| !strcmp(argv[0], "tfan")) {
	specks_read_mesh(st, f, argc, argv, line);
    } else if(!strcmp(argv[0], "waveobj")) {
	specks_read_waveobj(st, argc, argv, line, fname);
	
    } else if(!strcmp(argv[0], "textcolor") && argc==2) {
	sscanf(argv[1], "%d", &s.rgba);

    } else if(!strcmp(argv[0], "maxcomment") && argc==2) {
	int newmax = st->maxcomment;
	sscanf(argv[1], "%d", &newmax);
	if(newmax != st->maxcomment) {
	    SPFLUSH();
	    st->maxcomment = newmax;
	    tsl.bytesperspeck =
		(st->maxcomment+1 + (sizeof(s) - sizeof(s.title)) + 3) & ~3;
	    maxnsp = sizeof(speckbuf) / tsl.bytesperspeck;
	}
teuben's avatar
teuben committed
    } else {
	struct valdesc *vdp;
teuben's avatar
teuben committed

	k = getfloats( &s.p.x[0], 3, ignorefirst, argc, argv );
	if(k < 3) {
	    msg("Unrecognized datacmd: %s", line);
teuben's avatar
teuben committed
	    continue;
	i = ignorefirst + k;
teuben's avatar
teuben committed
	vdp = &st->vdesc[st->curdata][0];
	k = getfloats( &s.val[0], COUNT(s.val), i, argc, argv );
	for(m = 0; m < k; m++) {
	    if(vdp->nsamples++ == 0) {
		vdp->min = vdp->max = s.val[m];
	    } else {
		if(vdp->min > s.val[m]) vdp->min = s.val[m];
		else if(vdp->max < s.val[m]) vdp->max = s.val[m];
teuben's avatar
teuben committed
	    }
	    vdp->sum += s.val[m];
	    vdp->mean = vdp->sum / vdp->nsamples;
	    vdp++;
teuben's avatar
teuben committed
	}

	if(maxfields < k) maxfields = k;
	m = i+k;
teuben's avatar
teuben committed
	s.title[0] = '\0';
	    if(!strcmp(argv[m], "text")) {
		s.size = 1;
		if(++m < argc-1 && !strcmp(argv[m], "-size")) {
		    sscanf(argv[m+1], "%f", &s.size);
		    m += 2;
		}
		addchunk( st, 1, SMALLSPECKSIZE(0),
			speckscale, &s,
			rejoinargs(m, argc, argv),
			SMALLSPECKSIZE(0) );
teuben's avatar
teuben committed
	    }
	    else if(!strcmp(argv[m], "ellipsoid")) {
		specks_read_ellipsoid( st, &s.p, argc-m, argv+m, comment );
teuben's avatar
teuben committed

	    } else {
		*sp = s;
		nsp++;
		sp = NextSpeck( speckbuf, &tsl, nsp );
teuben's avatar
teuben committed
	    }
slevy's avatar
slevy committed
	    char *title = comment + (comment[1] == ' ' ? 2 : 1);
	    *sp = s;
	    strncpyt(sp->title, title, st->maxcomment+1);
	    maxfields = MAXVAL+1;	/* "keep titles too" */
	    nsp++;
	    sp = NextSpeck( speckbuf, &tsl, nsp );

	} else {
	    *sp = s;
	    nsp++;
	    sp = NextSpeck( speckbuf, &tsl, nsp );
teuben's avatar
teuben committed
	}
    }
  }
  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, "rb");
teuben's avatar
teuben committed
  char line[1024];
  int ntimes = -1, maxbox = -1, curbox = 0, curtime = 0, level = -1, i;
  int lno = 0;
teuben's avatar
teuben committed
  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 */
	specks_ensuretime( st, st->curdata, curtime+timebase );
teuben's avatar
teuben committed

	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;
  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 );
  parti_center( &pmid );
teuben's avatar
teuben committed

		/* round to 1 decimal */
slevy's avatar
slevy committed
#if THIEBAUX_VIRDIR
teuben's avatar
teuben committed
  {
    Point pmid, fwd, headv, pos, offset, newpos;
    static Point zvec = {0,0,1};
    float sz, scale;
    char cmd[128];
    sz = vdist( &box->p0, &box->p1 );
    sprintf(cmd, "%.1g", st->gscale * st->goboxscale * sz);
    scale = atof(cmd);
teuben's avatar
teuben committed
    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, "rb");
teuben's avatar
teuben committed
  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) {
teuben's avatar
teuben committed
	    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 == ':') {
teuben's avatar
teuben committed
		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
}



int specks_set_byvariable( struct stuff *st, char *str, int *val )
{
  int i;
  char *ep;
  int best = -1;
  if(str == NULL || str[0] == '\0') return 0;
teuben's avatar
teuben committed

  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) {
    CONST char *sign = (lat < 0) ? "-" : "+";
teuben's avatar
teuben committed
    lat = fabs(lat);
    lon /= 15;
    cp += sprintf(cp, hmdmfmt, (int)lon, 60. * (lon - (int)lon),
				sign, (int)lat, 60. * (lat - (int)lat),
				r);
  }
  return buf;
}

char *whereis(struct stuff *st, char *buf, Point *pos, int isvec)
teuben's avatar
teuben committed
{
  putcoords(buf, pos, NULL, isvec, "%.7g %.7g %.7g", NULL, NULL);
teuben's avatar
teuben committed
  if(strstr(st->altcoord[0].name, "2000")
	|| strstr(st->altcoord[0].name, "1950")
	|| strstr(st->altcoord[0].name, "eq")) {

    sprintf(buf + strlen(buf), "; %.9s: ", st->altcoord[0].name);
    putcoords(buf+strlen(buf), pos, &st->altcoord[0].w2coord, isvec,
		NULL, NULL, "%02d:%02.0f %s%02d:%02.0f %g");

  } else if(st->altcoord[0].name[0] != '\0') {
    sprintf(buf + strlen(buf), "; %.9s: ", st->altcoord[0].name);
    putcoords(buf+strlen(buf), pos, &st->altcoord[0].w2coord, isvec,
		NULL, "%.4f %.4f %g", NULL);
teuben's avatar
teuben committed
  }
  return buf;
}


static Point zero = {0,0,0}, forward = {0,0,-1};


static void tellwhere(struct stuff *st)
{
    Point pos, fwd, objpos, objfwd;
    Matrix cam2w, obj2w, w2obj, cam2obj;
    int id;
    char buf[180], obuf[64];
teuben's avatar
teuben committed

slevy's avatar
slevy committed
#ifdef THIEBAUX_VIRDIR
teuben's avatar
teuben committed
    CAVENavConvertCAVEToWorld( zero.x, pos.x );
    msg("cave at %s", whereis(st, buf, &pos, 0));
    CAVEGetPosition( CAVE_HEAD_NAV, pos.x );
    msg("head at %s", whereis(st, buf, &pos, 0));
teuben's avatar
teuben committed
    CAVEGetPosition( CAVE_WAND_NAV, pos.x );
    msg("wand at %s", whereis(st, buf, &pos, 0));
    CAVEGetVector( CAVE_WAND_FRONT_NAV, fwd.x );
    VD_get_cam2world_matrix( cam2w.m );
#else
    parti_getc2w( &cam2w );
#endif

    id = parti_idof( st );
    parti_geto2w( st, id, &obj2w );
    eucinv( &w2obj, &obj2w );

    scl = tfm2xyzaer( &pos, aer, &cam2w );
    vtfmpoint( &objpos, &pos, &w2obj );
teuben's avatar
teuben committed
    vtfmvector( &fwd, &forward, &cam2w );
    vtfmvector( &objfwd, &fwd, &w2obj );
    vunit( &objfwd, &objfwd );
    msg("camera at %s (w) %s (g%d)", whereis(st, buf, &pos, 0), whereis(st, obuf, &objpos, 0), id);
    msg("looking to %s (w) %s (g%d)", whereis(st, buf, &fwd, 1), whereis(st, obuf, &objfwd, 1), id);
    msg("jump %g %g %g  %g %g %g  %g",
	pos.x[0],pos.x[1],pos.x[2],
	aer[1],aer[0],aer[2],
	scl);
    msg("c2w: %g %g %g %g  %g %g %g %g  %g %g %g %g  %g %g %g %g",
	cam2w.m[0], cam2w.m[1], cam2w.m[2], cam2w.m[3],
	cam2w.m[4], cam2w.m[5], cam2w.m[6], cam2w.m[7],
	cam2w.m[8], cam2w.m[9], cam2w.m[10], cam2w.m[11],
	cam2w.m[12], cam2w.m[13], cam2w.m[14], cam2w.m[15]);
    mmmul( &cam2obj, &cam2w, &w2obj );
    msg("c2obj: %g %g %g %g  %g %g %g %g  %g %g %g %g  %g %g %g %g",
	cam2obj.m[0], cam2obj.m[1], cam2obj.m[2], cam2obj.m[3],
	cam2obj.m[4], cam2obj.m[5], cam2obj.m[6], cam2obj.m[7],
	cam2obj.m[8], cam2obj.m[9], cam2obj.m[10], cam2obj.m[11],
	cam2obj.m[12], cam2obj.m[13], cam2obj.m[14], cam2obj.m[15]);
teuben's avatar
teuben committed
}

int getbool( char *str, int defval ) {
teuben's avatar
teuben committed
  int v;
  char *ep;
  if(str == NULL) return defval;

  if(!strcasecmp(str, "on")) return 1;
  if(!strcasecmp(str, "off")) return 0;
slevy's avatar
 
slevy committed
  if(!strcasecmp(str, "toggle")) return !defval;
teuben's avatar
teuben committed
  if(!strcasecmp(str, "all")) return -1;
  v = strtol(str, &ep, 0);
  if(str == ep) return defval;
  return v;
}

slevy's avatar
 
slevy committed
double getfloat( char *str, double defval ) {
  double v;
teuben's avatar
teuben committed
  char *ep;
  int prefix = 0;
  if(str == NULL) return defval;
  if(str[0] == '-' && (str[1] == '=' || str[1] == '-'))	/* -=, -- */
    prefix = *str++;
  if(str[0] == '*' || str[0] == '+' || str[0] == '/' || str[0] == 'x')
    prefix = *str++;
  if(str[0] == '=')
    str++;
  v = strtod(str, &ep);
  if(ep == str) {
    v = defval;
  } else {
    switch(prefix) {
    case '-': v = defval - v; break;
    case '+': v = defval + v; break;
    case '*': v = defval * v; break;
    case '/': v = (v != 0) ? defval / v : 0; break;
    }  /* default: just use v */
  }
  return v;
}

int getfloats( float *v, int nfloats, int arg0, int argc, char **argv ) {
  int i;
  char *ep;
  for(i = 0; i < nfloats && arg0+i < argc; i++) {
    float tv = strtod( argv[arg0+i], &ep );
    if(ep == argv[arg0+i])
	break;
    v[i] = tv;
  }
  return i;
}

void editcmap( struct stuff *st, int ncmap, struct cment *cmap, char *range,
		char *whose, char *colorstr )
{
    int i, min, max;
    char *cp;
    struct cment cm;

    cp = colorstr;
    while(isspace(*cp)) cp++;
    if(*cp == ':' || *cp == '=') {
	char junk;
	do cp++; while(*cp == '=');
	if(sscanf(cp, "%d%c", &i, &junk) != 1) {
	    msg("%s: expected entrynumber(s) := oldindex, not %s", whose, colorstr);
	    return;
	}
	if(i < 0 || i >= ncmap) {
	    msg("%s: clamping index %d to 0..%d", whose, i, ncmap-1);
	    i = (i < 0) ? 0 : ncmap-1;
	}
	cm = cmap[i];
    } else {
	float r, g, b;
	if(sscanf(cp, "%f%f%f", &r,&g,&b) != 3) {
	    msg("%s: expected entrynumber(s) r g b, not %s", whose, colorstr);
	    return;
	}
	cm.raw = PACKRGBA( (int)(r*255), (int)(g*255), (int)(b*255), 0 );
	cm.cooked = specks_cookcment( st, cm.raw );
    }

    for(cp = range; ; cp++) {
	while(isspace(*cp) || *cp == ',') cp++;
	if((i = sscanf(cp, "%d-%d", &min, &max)) > 0) {
	    if(i == 1) max = min;
	} else if((i = sscanf(cp, ">%d", &min)) > 0) {
	    max = ncmap-1;
	} else if((i = sscanf(cp, "<%d", &max)) > 0) {
	    min = 0;
	}
	if(i > 0) {
	    if(min < 0) min = 0;
	    if(max >= ncmap) max = ncmap-1;
	    for(i = min; i <= max; i++)
		cmap[i] = cm;
	}
	while(*cp != ',' && !isspace(*cp)) {
	    if(*cp == '\0') return;
	    cp++;
	}
    }
}
teuben's avatar
teuben committed

int
specks_parse_args( struct stuff **stp, int argc, char *argv[] )
{
  int i;
  struct stuff *st = *stp;

  if(parti_parse_args( stp, argc, argv, NULL ) > 0)
  while( argc>0 &&
	(!strncmp( argv[0], "specks", 4 ) ||
	 !strcmp( argv[0], "feed" ) ||
	 !strcmp( argv[0], "eval" )) ) {
teuben's avatar
teuben committed
    argc--, argv++;
    /* VD_select_menu( specks_menuindex ); */
  }

  if(argc <= 0)
    return 0;

  if( st->dyn.enabled && st->dyn.ctlcmd &&
		(*st->dyn.ctlcmd)( &st->dyn, st, argc, argv ) ) {
    /* OK, dyn command handled it */

  } else if(!strcmp( argv[0], "?" ) || !strcmp( argv[0], "help" )) {
teuben's avatar
teuben committed
    static char *help1[] = {
"specks commands:",
" speed   data-steps per VirDir second",
" step N  -or-  step +N  -or-  step -N  Go to data step N, or step fwd/back",
slevy's avatar
 
slevy committed
" trange on|off|MIN MAX [WRAP]	limit range of datastep times",
" run				toggle auto-play (run/step)",
teuben's avatar
teuben committed
" color VARNO-or-NAME		color particles by VARNO'th variable (0..%d)",
" color const R G B		set all particles to be that color",
" lum   VARNO-or-NAME		tie particle size/luminosity to VARNOth var",
" lum   const LUM		set all particles to be brightness LUM",
" slum  SCALEFACTOR		scale particle brightness by SCALEFACTOR",
" psize SIZE			scale particle brightness by SIZE * SCALEFACTOR",
slevy's avatar
 
slevy committed
" depthsort			sort polygons by depth",
" see   DATASETNO-or-NAME	show that dataset (e.g. \"seedata 0\" or \"seedata gas\")",
teuben's avatar
teuben committed
" read  [-t time] DATAFILENAME	read data file (e.g. to add new specks)",
" ieee  [-t time] IEEEIOFILE	read IEEEIO file (starting at given timestep)",
" sdb   [-t time] SDBFILE	read .sdb star-data file",
" annot [-t time] string	set annotation string (for given timestep)",
slevy's avatar
 
slevy committed
" add  DATAFILECOMMAND		enter a single datafile command (ditto)",
teuben's avatar
teuben committed
" every N			subsample: show every Nth particle",
" bound				show bounds (coordinate range of all particles)",
" clipbox {on | off | X0,X1 Y0,Y1 Z0,Z1 | CENX,Y,Z RADX,Y,Z | X0 Y0 Z0  X1 Y1 Z1} clipping region",
" add box [-n boxno] [-l level] CENX,Y,Z RX,RY,RZ | X0 Y0 Z0 X1 Y1 Z1  marker-box",
" boxlabels                     show box numbers",
" boxaxes                       show R/G/B axes from X0,Y0,Z0 box corner"
" boxes {off|on|only}		hide/show all AMR boxes",
" {hide|show} LEVELNO ...	hide/show AMR boxes of those levels (or \"all\")",
" {point|polygon|texture} {on|off}",
teuben's avatar
teuben committed
" fmenu HEIGHT  -or-  fmenu XPOS YPOS  -or- fmenu wall WALLNO",
#else
" readpath  FILENAME.wf		read Wavefront camera path (from virdir \"wfout\")",
" play  SPEED[f]		play path (at SPEED times normal speed)",
"			(with \"f\" suffix, play every SPEEDth frame)",
" frame FRAMENO			go to Nth frame",
" focal FOCALLEN		focal length (determines fly/tran speed)",
" clip  NEAR FAR		clipping distances",
" jump  X Y Z [RX RY RZ]	put viewpoint there",
" center X Y Z			set center of rotation for orbit/rotate",
" censize RADIUS		size of center marker",
teuben's avatar
teuben committed
" snapset  filestem [frameno]	set snapshot parameters",
" snapshot [frameno]		take snapshot [uses convert(1)]",
slevy's avatar
 
slevy committed
" kira {node|ring|size|scale|span|track}  starlab controls; try \"kira ?\"",
teuben's avatar
teuben committed
#endif
    };

    for(i = 0; i < COUNT(help1); i++)
	msg(help1[i], MAXVAL-1);

    if(st->dyn.enabled && st->dyn.help)
	(*st->dyn.help)(&st->dyn, st, 0);
teuben's avatar
teuben committed
  

  } else if(!strcmp( argv[0], "read" )) {
	if(argc > 1) {
	    char *foundfile = findfile( NULL, argv[1] );
	    if(foundfile == NULL)
slevy's avatar
slevy committed
		msg("read: can't find file %s", argv[1]);
	    else
		specks_read( &st, foundfile );
	}
teuben's avatar
teuben committed

  } else if(!strcmp( argv[0], "include" )) {
#ifdef NOTYET
#endif

  } else if( !strcmp(argv[0], "on") || !strcmp(argv[0], "off")
	    || !strcmp(argv[0], "enable") || !strcmp(argv[0], "disable") ) {

	st->useme = argc>1 ? getbool(argv[1], st->useme) : (argv[0][1]=='n');
	msg(st->useme ? "enabled" : "disabled");
	
slevy's avatar
slevy committed
  } else if(!strcmp(argv[0], "echo")) {
	msg( "%s", rejoinargs(1, argc, argv) );

  } else if(!strcmp(argv[0], "add")) {
teuben's avatar
teuben committed
	int k, io[2];
	FILE *tf;
	char fdname[64+L_tmpnam];

#ifdef WIN32
	tmpnam(fdname);
	tf = fopen(fdname, "w");
#else /* unix */
	pipe(io);
	sprintf(fdname, "/dev/fd/%d", io[0]);
	tf = fdopen(io[1], "w");
#endif
	if(tf == NULL) {
	    fprintf(stderr, "Yeow: can't make temp file?\n");
	} else {
	    for(k = 1; k < argc; k++)
		fprintf(tf, "%s ", argv[k]);
	    fprintf(tf, "\n");
	    fclose(tf);
	    specks_read( &st, fdname );
#ifdef WIN32
teuben's avatar
teuben committed
#endif
	}

#ifndef WIN32
	close(io[0]);
#endif

slevy's avatar
slevy committed
  } else if(!strcmp( argv[0], "async" ) ||
	    !strcmp( argv[0], "|" )) {
	specks_add_async( st, rejoinargs( 1, argc, argv ),
				argv[0][0] == '|' );
teuben's avatar
teuben committed

  } else if(!strcmp( argv[0], "update" )) {
	parti_update();

  } else if(!strcmp( argv[0], "hist" )) {
	register struct specklist *sl;
	register struct speck *sp;
	int nspecks, nclipped, nthreshed, nlow, nhigh, nundefined;
	int clipping = (st->clipbox.level > 0);
	int threshing = (st->usethresh & P_USETHRESH) && (st->seesel.wanted != 0);
teuben's avatar
teuben committed
	int nbuckets = 11;
	int dolog = 0;
	int *bucket;
	float v, vmin, vmax, vrange;
	int histvar;
	int i, k, bno;
	struct valdesc *vd;

	for(i=1,k=1; i+1 < argc; k++) {
	    int yes = argv[i][0] == '-';
	    if(argv[i][0] != '-' && argv[i][1] != '+') break;
	    switch(argv[i][k]) {
		case 't': threshing = yes ? THRESHBIT : 0; break;
		case 'c':
		case 'b': clipping = yes; break;
		case 'n':
		    sscanf(argv[i][++k] ? &argv[i][k] : argv[++i],
			"%d", &nbuckets);
		    i++; k=0;
		    break;
		case 'l': dolog = yes; break;
		case '\0': i++; k=0;
		default: i=argc; break;
teuben's avatar
teuben committed
	    }
	}

	if(i>=argc) {
	    msg("Usage: hist [-t|+t(threshed)] [-c|+c(clipped)] [-n nbuckets] [-l(logscale)]  datavar [min max]");
	    return 1;
	}
teuben's avatar
teuben committed
	if(!specks_set_byvariable( st, argv[i], &histvar )) {
	    msg("hist %s: expected name/index of data variable", argv[i]);
	    return 1;
	}

	vd = &st->vdesc[st->curdata][histvar];
	vmin = vd->min;
	vmax = vd->max;

	if(i+1<argc) sscanf(argv[i+1], "%f", &vmin);
	if(i+2<argc) sscanf(argv[i+2], "%f", &vmax);

	if(vmax < vmin)
	    v = vmax, vmax = vmin, vmin = v;

	if(dolog && (vmin <= 0 || vmax <= 0)) {
	    msg("hist: can't take logs (-l) if range includes zero!");
	    dolog = 0;
	}
	if(dolog) {
	    vmin = log(vmin);
	    vmax = log(vmax);
	}
	vrange = (nbuckets-1) / ((vmax>vmin) ? vmax - vmin : 1);

	if(nbuckets<=0 || nbuckets>20000) {
	    msg("hist -n %d: Incredible number of histogram buckets", nbuckets);
	    return -1;
	}

	bucket = NewA(int, nbuckets);
	memset(bucket, 0, nbuckets*sizeof(int));

	nspecks = nclipped = nthreshed = nundefined = nlow = nhigh = 0;
	for(sl = st->sl; sl != NULL; sl = sl->next) {
	    if((sp = sl->specks) == NULL || sl->text != NULL)
		continue;
	    nspecks += sl->nspecks;
	    if(sl->bytesperspeck < SMALLSPECKSIZE(histvar+1)) {
		nundefined += sl->nspecks;
		continue;
	    }
	    for(i = sl->nspecks; --i >= 0; sp = NextSpeck( sp, sl, 1 )) {
		if(clipping &&
		  (sp->p.x[0] < st->clipbox.p0.x[0] ||
		   sp->p.x[0] > st->clipbox.p1.x[0] ||
		   sp->p.x[1] < st->clipbox.p0.x[1] ||
		   sp->p.x[1] > st->clipbox.p1.x[1] ||
		   sp->p.x[2] < st->clipbox.p0.x[2] ||
		   sp->p.x[2] > st->clipbox.p1.x[2])) {
			nclipped++;
			continue;
		}
		if(!SELECTED(sl->sel[i], &st->seesel)) {
		    nthreshed++;
		    continue;
teuben's avatar
teuben committed
		}
		v = sp->val[histvar];
		if(dolog) {
		    if(v <= 0) {
			nundefined++;
			continue;
		    }
		    v = log(v);
		}
		bno = (int) ((v - vmin)*vrange);
		if(bno < 0) nlow++;
		else if(bno >= nbuckets) nhigh++;
		else bucket[bno]++;
	    }
	}
	if(nspecks == 0) {
	    msg("No specks loaded yet");
	} else {
	    msg("hist -n %d %s%s%s%d(%s) %g %g => ",
		nbuckets, dolog?"-l ":"", clipping?"-c ":"", threshing?"-t ":"",
		histvar, vd->name,
		dolog ? exp(vmin) : vmin,
		dolog ? exp(vmax) : vmax);
	    msg("Total %d, %d < min, %d > max, %d undefined, %d clipped, %d threshed",
		nspecks, nlow, nhigh, nundefined, nclipped, nthreshed);
	    k = nlow;
	    msg("%d\t< %g", nlow, dolog ? exp(vmin) : vmin);
	    for(i = 0; i < nbuckets; i++) {
		v = vmin + ( (vrange>0) ? i / vrange : 0 );
		msg("%d\t>= %g", bucket[i], dolog ? exp(v) : v);
	    }
	    msg("%d\t> %g", nhigh, dolog ? exp(vmax) : vmax);
	}


  } else if(!strcmp( argv[0], "bound" )) {
	register struct specklist *sl;
	register struct speck *sp;
	Point xmin,xmax, mean, mid, radius;
	int minmaxk = MAXVAL+1, maxmaxk = 0;
	int nspecks = 0;
	Matrix To2w;
	int hasT = 0;
	if(argc > 1) {
	    hasT = 1;
	    parti_geto2w( st, parti_idof( st ), &To2w );
teuben's avatar
teuben committed

	xmin.x[0] = xmin.x[1] = xmin.x[2] = 1e38;
	xmax.x[0] = xmax.x[1] = xmax.x[2] = -1e38;
	mean.x[0] = mean.x[1] = mean.x[2] = 0;
	for(sl = st->sl; sl != NULL; sl = sl->next) {
	    int i, maxk;
	    if((sp = sl->specks) == NULL)
		continue;
	    for(maxk=0; maxk<MAXVAL && SMALLSPECKSIZE(maxk)<sl->bytesperspeck; maxk++)
		;
	    if(minmaxk > maxk) minmaxk = maxk;
	    if(maxmaxk < maxk) maxmaxk = maxk;
	    for(i = sl->nspecks; --i >= 0; sp = NextSpeck( sp, sl, 1 )) {
		Point tp, *p = &sp->p;
		if(hasT) {
		    vtfmpoint( &tp, p, &To2w );
		    p = &tp;
		}
		if(xmin.x[0] > p->x[0]) xmin.x[0] = p->x[0];
		if(xmax.x[0] < p->x[0]) xmax.x[0] = p->x[0];
		mean.x[0] += p->x[0];
		if(xmin.x[1] > p->x[1]) xmin.x[1] = p->x[1];
		if(xmax.x[1] < p->x[1]) xmax.x[1] = p->x[1];
		mean.x[1] += p->x[1];
		if(xmin.x[2] > p->x[2]) xmin.x[2] = p->x[2];
		if(xmax.x[2] < p->x[2]) xmax.x[2] = p->x[2];
		mean.x[2] += p->x[2];
teuben's avatar
teuben committed
	    }
	    nspecks += sl->nspecks;
	}
	if(nspecks == 0) {
	    msg("No specks loaded yet");
	} else {
	    CONST char *which = hasT ? " (world)" : " (object)";
teuben's avatar
teuben committed
	    vscale( &mean, 1.0/nspecks, &mean );
	    vcomb( &mid, .5,&xmin, .5,&xmax );
	    vcomb( &radius, .5,&xmax, -.5,&xmin );
	    msg( "%d specks in range %g %g %g .. %g %g %g%s",
teuben's avatar
teuben committed
		nspecks, xmin.x[0],xmin.x[1],xmin.x[2],
		xmax.x[0],xmax.x[1],xmax.x[2], which);
	    msg( "midbbox %g %g %g  boxradius %g %g %g%s",
teuben's avatar
teuben committed
		mid.x[0],mid.x[1],mid.x[2],
		radius.x[0],radius.x[1],radius.x[2], which);
	    msg( "mean %g %g %g%s", mean.x[0],mean.x[1],mean.x[2], which );
teuben's avatar
teuben committed
	}

  } else if(!strcmp( argv[0], "fspeed" )) {
	if(argc>1) {
teuben's avatar
teuben committed
	    specks_set_fspeed( st, getfloat(argv[1], st->fspeed) );
teuben's avatar
teuben committed
	    st->playnext = 0.0;
	}
	msg("fspeed %g steps per real-time second", st->fspeed);

  } else if(!strcmp( argv[0], "speed" )) {
	if(argc>1)
slevy's avatar
 
slevy committed
	    specks_set_speed( st, getfloat(argv[1], clock_speed(st->clk)) );
	msg("speed %g steps per anim second", clock_speed(st->clk));

  } else if(!strcmp( argv[0], "timealign" )) {

	float datatime = 0, realtime = 0;
	/* Compute timebase so datastep <datatime> falls at <clocktime>,
	 * given current speed setting.
	 */
	double speed = clock_speed(st->clk) * st->clk->fwd;
	double timebase;
	if(getfloats( &datatime, 1, 1, argc,argv ) > 0 &&