Skip to content
Snippets Groups Projects
partibrains.c 178 KiB
Newer Older
  if(slhead == NULL) return 0;

teuben's avatar
teuben committed
  for(hi = 0, i = 0; hi < nhits && i < nents; hi++, i += ns + 3) {
    ns = hitbuf[i];
    if(ns < 1 || ns > 16)
	break;			/* trouble */
    z0 = hitbuf[i+1];

    if(id != hitbuf[i+3] || ns <= 1) continue;

teuben's avatar
teuben committed
    if(st->usepoly>1)		/* debug */
	printf(ns>1?"[%x %d/%d]":"[%x %d]", z0, hitbuf[i+3],hitbuf[i+4]);

    if(bestz > z0 && ns > 1 && (slno = hitbuf[i+4]) > 0) {
teuben's avatar
teuben committed
	bestz = z0;
	bestslno = slno;
	bestspeck = (ns>2) ? hitbuf[i+5] : 0;
    }
    if(st->picked != NULL && ns > 2) {
	for(sl = slhead, slno = 1; sl != NULL && slno < hitbuf[i+4]; sl = sl->next, slno++)
	    ;
	if(sl && hitbuf[i+5] < sl->nspecks)
	    if((*st->picked)(st, &hitbuf[i], sl, hitbuf[i+5]))
		return 0;
    }
  }

  if(st->picked != NULL) {
    if((*st->picked)(st, NULL, NULL, 0))
	return 0;
teuben's avatar
teuben committed
  }
  if(bestslno <= 0)
    return 0;

  for(sl = slhead, slno = 1; sl != NULL; sl = sl->next, slno++) {
    if(slno == bestslno) {
	if(bestspeck < 0 || bestspeck >= sl->nspecks) {
	    msg("Bogus pick result: sl %x bestspeck %d of 0..%d!\n",
		sl, bestspeck, sl->nspecks-1);
	    return 0;
	}
	if(slp) *slp = sl;
	if(specknop) *specknop = bestspeck;
	if(pos) {
	    if(bestspeck < 0 || bestspeck >= sl->nspecks)
		bestspeck = 0;
	    *pos = NextSpeck( sl->specks, sl, bestspeck )->p;
	}
	*bestzp = bestz;
	return 1;
    }
  }
  return 0;
}

int selname( struct stuff *st, char *name, int create ) {
    int i, avail;
    if(name == NULL) return 0;
    if(!strcmp(name, "all") || !strcmp(name, "on")) return SEL_ALL;
    if(!strcmp(name, "none") || !strcmp(name, "off")) return SEL_OFF;
    if(!strcmp(name, "thresh")) return SEL_THRESH;
    if(!strcmp(name, "pick")) return SEL_PICK;
    for(i = COUNT(st->selitems); --i >= 0; ) {
	if(!strcmp(name, st->selitems[i].name))
	    return i+1;
	if(st->selitems[i].name[0] == '\0') avail = i;
    }
    if(create) {
	sprintf(st->selitems[avail].name, "%.15s", name);
	return avail+1;
    }
    return 0;
}

void selinit( SelOp *sp ) {
    sp->wanted = 0;
    sp->wanton = 0;
    sp->use = SEL_NONE;
}

static char *selcat( struct stuff *st, char *from, int selno, int preop ) {
    char *what = "";
    static char s[8];
    switch(selno) {
    case SEL_NONE: what = "off"; break;
    case SEL_ALL: what = "all"; break;
    case SEL_THRESH: what = "thresh"; break;
    case SEL_PICK: what = "pick"; break;
    default:
	if(selno > 0 && selno <= MAXSEL && st->selitems[selno-1].name[0] != '\0')
	    what = &st->selitems[selno-1].name[0];
	else {
	    sprintf(s, "%d", selno<0||selno>MAXSEL?0:selno);
	    what = s;
	}
    }
    if(preop)
	*from++ = preop;
    return from + sprintf(from, "%.15s ", what);
}


char *show_selexpr( struct stuff *st, SelOp *destp, SelOp *srcp )
{
    char str[(MAXSELNAME + 2)*33];
    char *tail = str;
    static char *sstr;
    static int sroom = -1;
    int i;

    if(destp) {
	if(destp->wanted == 0) {
	    tail = selcat(st, tail, SEL_ALL, 0 );
	} else {
	    for(i = SEL_THRESH; i > 0; i--) {
		if(~destp->wanted & SELMASK(i)) {
		    tail = selcat( st, tail, i,
				destp->wanton & SELMASK(i) ? 0 : '-' );
		} else if(destp->wanton & SELMASK(i)) {
		    tail = selcat( st, tail, i, '^' );
		}
	    }
	}
	if(srcp)
	    tail += sprintf(tail, "= ");
	if(srcp->use == SEL_NONE) {
	    tail = selcat( st, tail, srcp->use, 0 );
	} else if(srcp->use == SEL_USE && srcp->wanted == 0) {
	    tail = selcat( st, tail, SEL_ALL, 0 );
	} else {
	    for(i = SEL_THRESH; i > 0; i--) {
		if(srcp->wanted & SELMASK(i)) {
		    tail = selcat( st, tail, i,
				srcp->wanton & SELMASK(i) ? 0 : '-' );
		} else if(srcp->wanton & SELMASK(i)) {
		    tail = selcat( st, tail, i, '^' );
		}
	    }
	}
    }
    if(tail - str > sroom) {
	if(sstr) Free(sstr);
	sroom = tail - str + 60;
	sstr = NewN( char, sroom );
    }
    if(tail == str) strcpy(sstr, "null");
    else {
	tail[-1] = '\0';
	strcpy(sstr, str);
    }
    return sstr;
}

enum SelToken {
    SEL_ERR,		/* unknown */
    SEL_EOF,
    SEL_WORD,		/* bare word or +word */
    SEL_EQUALS		/* = */
};

static enum SelToken seltoken( char **sp, int *leadcp, char word[MAXSELNAME] ) {
    char *p = *sp;
    int k;
    *leadcp = '\0';
    word[0] = '\0';
    while(isspace(*p)) p++;
    if(*p == '\0')
	return SEL_EOF;
    if(*p == '-' || *p == '^' || *p == '+')
	*leadcp = *p++;
    if(*p == '=') {
	*sp = p+1;
	return SEL_EQUALS;
    }
    if(!(isalnum(*p) || *p == '_'))
	return SEL_ERR;
    word[0] = *p++;
    for(k = 1; isalnum(*p) || *p == '_'; p++) {
	if(k < MAXSELNAME-1) word[k++] = *p;
    }
    word[k] = '\0';
    *sp = p;
    return SEL_WORD;
void seldest2src( struct stuff *st, CONST SelOp *dest, SelOp *src ) {
    selinit( src );
    if(dest->use == SEL_USE) {
	*src = *dest;
    } else if(dest->use == SEL_DEST) {
	src->wanted = ~dest->wanted | dest->wanton;
	src->wanton = dest->wanton;
	src->use = SEL_USE;
void selsrc2dest( struct stuff *st, CONST SelOp *src, SelOp *dest ) {
    *dest = *src;
    if(src->use == SEL_USE) {
	dest->wanted = ~src->wanted;
	dest->wanton = src->wanton & src->wanted;
	dest->use = SEL_DEST;
    }
}

void seldestinvert( struct stuff *st, SelOp *dest ) {
    dest->wanton ^= ~dest->wanted;
}

int seldest( struct stuff *st, SelOp *dest, int selno, int selch ) {
    SelMask mask;
    if(dest == NULL)
	return 0;
    if(selno == SEL_ALL) {
	mask = ~0;
    } else if(selno <= 0 || selno > 32) {
	return 0;
    } else {
	mask = SELMASK(selno);
    }
    if(dest->use != SEL_DEST) {
	dest->use = SEL_DEST;
	dest->wanted = ~0;
	dest->wanton = 0;
    }
    switch(selch) {
    case '^':
	dest->wanted |= mask;
	dest->wanton |= mask;
	break;
    case '-':
	dest->wanted &= ~mask;
	dest->wanton &= ~mask;
	break;
    case '+':
    case '\0':
	dest->wanted &= ~mask;
	dest->wanton |= mask;
	break;
    default:
int selsrc( struct stuff *st, SelOp *src, int selno, int selch ) {
    SelMask mask;
    if(src == NULL)
	return 0;
    if(src->use == SEL_DEST) {
	src->wanted = 0;
	src->wanton = 0;
	src->use = SEL_USE;
    }
    if(selno == SEL_OFF) {
	src->wanted = src->wanton = mask = ~0;
	src->use = SEL_NONE;
    } else if(selno == SEL_ALL) {
	src->wanted = src->wanton = mask = 0;
    } else if(selno > 0 && selno <= 32) {
	mask = SELMASK(selno);
	src->use = SEL_USE;
    } else {
	return 0;
    }
    switch(selch) {
    case '-':
	src->wanted |= mask;
	src->wanton &= ~mask;
	break;
    case '+':
    case '\0':
	src->wanted |= mask;
	src->wanton |= mask;
	break;
    default:
	return 0;
    }
    return 1;
}
/*
 * [word [op]=] { [op]word }*
 */
int parse_selexpr( struct stuff *st, char *str, SelOp *destp, SelOp *srcp, char *plaint )
{
  SelOp dest, src;
  char wd[MAXSELNAME], w[MAXSELNAME];
  char *p = str;
  int leadc, leadc2;
  int ok = 1;
  int anysrc = 0, anydest = 0;

  selinit(&dest); selinit(&src);
  tok = seltoken( &p, &leadc, wd );
  if(tok == SEL_EOF)
      return 0;
  if(destp) {
      /* Had better begin with a word, and with no prefix */
    int destno;
    if(tok != SEL_WORD) goto fail;
    destno = selname( st, wd, 1 );
    tok2 = seltoken( &p, &leadc2, w );
    if(tok2 == SEL_ERR) goto fail;
    ok = seldest( st, &dest, destno, leadc ? leadc : leadc2 );
    anydest = 1;
    if(!ok) goto fail;
    if(tok2 == SEL_WORD) {
	tok = SEL_WORD;
	leadc = leadc2;
	strcpy(wd, w);
    } else {
	tok = seltoken( &p, &leadc, wd );
    }
  }
  while(srcp && tok == SEL_WORD) {
    ok = selsrc( st, &src, selname( st, wd, 0 ), leadc );
    if(!ok) goto fail;
    if(src.use == SEL_NONE && src.wanted != 0 && destp) {
	seldestinvert( st, &dest );
	selsrc( st, &src, SEL_ALL, 0 );	/* X = off  <=> -X = on */
    }
    anysrc = 1;
    tok = seltoken( &p, &leadc, wd );
  if(tok != SEL_EOF)
      goto fail;
  if(destp && anydest) *destp = dest;
  if(srcp && anysrc) *srcp = src;
  return (anydest?1:0) + (anysrc?2:0);
  if(plaint)
      msg("%s: um, %s", plaint, str);
  return 0; /*XXX*/
}

CONST char *selcounts( struct stuff *st, struct specklist *sl, SelOp *selp ) {
    int i;
    static char counts[24];
    int yes, total;
    SelOp selop;

    if(selp == NULL) return "";
    if(selp->use == SEL_DEST)
	seldest2src( st, selp, &selop );
    else
	selop = *selp;
    for(yes = total = 0; sl != NULL; sl = sl->next) {
	SelMask *sel = sl->sel;
	if(sl->text != NULL || sl->nsel < sl->nspecks || sl->special != SPECKS)
	    continue;
	total += sl->nspecks;
	for(i = 0; i < sl->nspecks; i++)
	    if(SELECTED(sel[i], &selop))
		yes++;
    }
    if(selop.use == SEL_NONE) yes = 0;
    sprintf(counts, "%d of %d", yes, total);
    return counts;
}

SpecksPickFunc specks_all_picks( struct stuff *st, SpecksPickFunc func, void *arg ) {
    SpecksPickFunc was = st->picked;
    st->picked = func;
    st->pickinfo = arg;
    return was;
}
teuben's avatar
teuben committed


static void addchunk( struct stuff *st, int nsp, int bytesperspeck,
			float scaledby, struct speck *sp, char *text,
			int outbytesperspeck )
{
    struct specklist **slp, *sl = NewN( struct specklist, 1 );
    memset( sl, 0, sizeof(*sl) );
    sl->speckseq = ++st->speckseq;
teuben's avatar
teuben committed

    sl->bytesperspeck = outbytesperspeck;

    sl->specks = NewNSpeck( sl, nsp );
    sl->nspecks = nsp;
    sl->scaledby = scaledby;
    if(text) {
	sl->text = NewN( char, strlen(text)+1 );
	strcpy(sl->text, text);
    } else {
	sl->text = NULL;
    }
    if(bytesperspeck == outbytesperspeck) {
	memcpy( sl->specks, sp, nsp*sl->bytesperspeck );
    } else {
	int i;
	for(i = 0; i < nsp; i++)
	    memcpy(((char *)sl->specks) + i*outbytesperspeck,
		   ((char *)sp) + i*bytesperspeck,
		   outbytesperspeck);
    }
    sl->sel = NewN( SelMask, nsp );
    sl->nsel = nsp;
    memset(sl->sel, 0, nsp*sizeof(SelMask));

teuben's avatar
teuben committed
    slp = specks_timespecksptr(st, st->curdata, st->datatime);
    sl->next = *slp;
    *slp = sl;
    st->sl = specks_timespecks(st, st->curdata, st->curtime); /* in case it changed */
    sl->colorseq = -1;		/* Force recomputing colors */
    sl->sizeseq = -1;		/* Force recomputing sizes */
}

int specks_count( struct specklist *sl ) {
    int n;
    for(n = 0; sl != NULL; sl = sl->next)
	if(sl->text == NULL)
	    n += sl->nspecks * (sl->subsampled>0 ? sl->subsampled : 1);
    return n;
}

teuben's avatar
teuben committed
static float speckscale = 1;

#define LINEBUFSIZE 2048

static char *getline(FILE *f, char *buf) {
  buf[0] = '\0';
  while(fgets(buf, LINEBUFSIZE, f) != NULL) {
    char *cp = buf;
    while(isspace(*cp)) cp++;
    if(*cp != '#' && *cp != '\0')
	return cp;
  }
  return NULL;
}
 
static enum SurfStyle getstyle( char *str )
{
  if(str == NULL || !strcmp(str, "solid") || !strncmp(str, "fill", 4)
		 || !strncmp(str, "poly", 4))
  if(!strncmp(str, "wire", 4) || !strncmp(str, "line", 4))
  if(!strncmp(str, "plane", 5) || !strncmp(str, "ax", 2))
  if(!strncmp(str, "point", 5))
  if(!strcmp(str, "off"))
  msg("Unknown surface style \"%s\": want solid|line|plane|point|off", str);
}

/*
 * Xcen Ycen Zcen ellipsoid [-t txno] [-c cindex] [-s surfstyle] [-n nu[,nv]] [-r sizex[,y,z]] [0-or-9-or-16-numbers-tfm]
 */
void specks_read_ellipsoid( struct stuff *st, Point *pos, int argc, char **argv, char *comment ) {
  int i;
  struct ellipsoid e, *ep;
  float m3[3*3];
slevy's avatar
slevy committed
  float rxyz[3];

  memset(&e, 0, sizeof(e));
  e.cindex = -1;
  e.pos = *pos;
  e.size.x[0] = e.size.x[1] = e.size.x[2] = 1;

  if(comment) {
    e.title = (comment[1] == ' ') ? comment+2 : comment+1;
  }

  for(i = 1; i+1 < argc; i += 2) {
    if(!strncmp(argv[i], "-c", 2)) {
	sscanf(argv[i+1], "%d", &e.cindex);
    } else if(!strncmp(argv[i], "-s", 2)) {
	e.style = getstyle(argv[i+1]);
    } else if(!strcmp(argv[i], "-r")) {
	int k = sscanf(argv[i+1], "%f%*c%f%*c%f", &e.size.x[0],&e.size.x[1],&e.size.x[2]);
	switch(k) {
	case 1: e.size.x[2] = e.size.x[1] = e.size.x[0]; break;
	case 3: break;
	default: msg("ellipsoid -r: expected 1 or 3 comma-separated semimajor axes not %s", argv[i+1]);
	}
    } else if(!strcmp(argv[i], "-n")) {
	int k = sscanf(argv[i+1], "%d%*c%d", &e.nu, &e.nv);
	switch(k) {
	case 1: e.nv = e.nu/2 + 1; break;
	case 2: break;
	default: msg("ellipsoid -n: expected nu or nu,nv but not %s", argv[i+1]);
	}
    } else if(!strcmp(argv[i], "-l")) {
	sscanf(argv[i+1], "%d", &e.level);
    } else {
	break;
    }
  }

  if(i == argc) {
    /* OK */
  } else if(i+9 == argc && 9==getfloats(m3, 9, i, argc, argv)) {
    memcpy(&e.ori.m[0*4+0], &m3[0], 3*sizeof(float));
    memcpy(&e.ori.m[1*4+0], &m3[3], 3*sizeof(float));
    memcpy(&e.ori.m[2*4+0], &m3[6], 3*sizeof(float));
    e.ori.m[3*4+3] = 1;
    e.hasori = 1;
  } else if(i+16 == argc && 16==getfloats(&e.ori.m[0], 16, i, argc, argv)) {
    e.hasori = 1;
slevy's avatar
slevy committed
  } else if(i+3 == argc && 3==getfloats(rxyz, 3, i, argc, argv)) {
    float aer[3];
    aer[0] = rxyz[1];
    aer[1] = rxyz[0];
    aer[2] = rxyz[2];
    xyzaer2tfm( &e.ori, NULL, aer );
    e.hasori = 1;
slevy's avatar
slevy committed
    msg("ellipsoid: expected 0 or 3 (RxRyRz) or 9 or 16 numbers after options, not %s",
	rejoinargs(i, argc, argv));
    return;
  }

  /* OK, take it */
  ep = NewN( struct ellipsoid, 1 );
  *ep = e;
  if(e.title) ep->title = shmstrdup(e.title);
  ep->next = st->staticellipsoids;
  st->staticellipsoids = ep;
}

void specks_read_mesh( struct stuff *st, FILE *f, int argc, char **argv, char *buf )
{
  char *cp, *ep;
  int i, count, alloced, err = 0;
  int havetx = 0;
  Point pts[300], txs[300];
  struct mesh *m = NewN( struct mesh, 1 );
  int timestep = st->datatime;

  memset(m, 0, sizeof(*m));
  m->type = QUADMESH;
  m->txno = -1;
  m->pts = pts; m->tx = txs;
  alloced = COUNT(pts);

  if(!strcmp(argv[0], "quadmesh") || !strcmp(argv[0], "mesh")) {
    m->type = QUADMESH;
  }

  for(i = 1; i+1 < argc; i += 2) {
    if(!strncmp(argv[i], "-time", 3)) {
	timestep = atoi(argv[i+1]);
    } else if(!strncmp(argv[i], "-t", 2)) {
	havetx = sscanf(argv[i+1], "%d", &m->txno);
    } else if(!strcmp(argv[i], "-static")) {
	timestep = -1;
	i--;
    } else if(!strncmp(argv[i], "-c", 2)) {
	sscanf(argv[i+1], "%d", &m->cindex);
    } else if(!strncmp(argv[i], "-s", 2)) {
	m->style = getstyle(argv[i+1]);
    } else {
	break;
    }
  }

  if(m->type == QUADMESH) {
    getline(f, buf);
    if(sscanf(buf, "%d%d", &m->nu, &m->nv) != 2
			|| m->nu <= 0 || m->nv <= 0) {
	msg("quadmesh: expected nu nv, got %s", buf);
	Free(m);
	return;
    }
    alloced = m->nverts = m->nu * m->nv;
    m->pts = NewN( Point, m->nverts );
    m->tx = havetx ? NewN( Point, m->nverts ) : NULL;
  }
    

  count = 0;
  while((cp = getline(f, buf)) != NULL) {
    int ngot;
    Point *tp;
#define BRA '{'
#define CKET '}'
    if(*cp == BRA) continue;
    if(*cp == CKET) break;

    if(count >= alloced) {
	if(m->type == QUADMESH) {
	    err = 1;
	    msg("Only expected %d*%d=%d vertices; what's %s",
		m->nu,m->nv, alloced, cp);
	    break;
	}
	/* otherwise, ask for more */
	/* XXX maybe later */
	break;
    }
	
    tp = &m->pts[count];
    for(ngot = 0; ngot < 3; ngot++, cp = ep) {
	tp->x[ngot] = strtod(cp, &ep);
	if(cp == ep) break;
    }
    if(havetx && ngot == 3) {
	tp = &m->tx[count];
	for(ngot = 0; ngot < 2; ngot++, cp = ep) {
	    tp->x[ngot] = strtod(cp, &ep);
	    if(cp == ep) break;
	}
	ngot++;
    }
    if(ngot != 3) {
	msg("Expected %d numbers per line; what's %s",
		havetx?5:3, buf); 
	err = 1;
	break;
    }
    count++;
  }
  if(m->type == QUADMESH && count < alloced) {
    err = 1;
    msg("Expected %d*%d = %d vertices, only got %d", m->nu,m->nv, alloced, count);
  }
  if(err) {
    if(m->pts != pts) Free(m->pts);
    if(m->tx != txs && m->tx != NULL) Free(m->tx);
    Free(m);
    return;
  }

  if(timestep >= 0) {
    if(timestep >= st->ntimes)
	specks_timespecksptr( st, st->curdata, timestep );
    m->next = st->meshes[st->curdata][timestep];
    st->meshes[st->curdata][timestep] = m;
  } else {
    m->next = st->staticmeshes;
    st->staticmeshes = m;
  }
}



void specks_read_waveobj( struct stuff *st, int argc, char **argv, char *line, char *infname )
{
  struct mesh *m = NewN( struct mesh, 1 );
  int lno;
  int i, f;
  int timestep = st->datatime;
  FILE *inf;
  char *fname, *fullname;
#define CHUNK 500
  int kpts, kfvs, kfv0, kfvn;
  int npts, nfvs, nfv0, nfvn;
  struct chain {
	void *data;
	int pos;
	struct chain *next;
  } *cpts, *cfvs, *cfv0, *cfvn;
  Point vpts[CHUNK];
  int vfvs[CHUNK], vfv0[CHUNK], vfvn[CHUNK];


  memset(m, 0, sizeof(*m));
  m->type = POLYMESH;
  m->cindex = -1;
  m->txno = -1;

  for(i = 1; i+1 < argc; i += 2) {
    if(!strncmp(argv[i], "-time", 2)) {
	timestep = atoi(argv[i+1]);
    } else if(!strncmp(argv[i], "-texture", 3) || !strncmp(argv[i], "-tx", 3)) {
	sscanf(argv[i+1], "%d", &m->txno);
    } else if(!strcmp(argv[i], "-static")) {
	timestep = -1;
	i--;
    } else if(!strncmp(argv[i], "-c", 2)) {
	sscanf(argv[i+1], "%d", &m->cindex);
    } else if(!strncmp(argv[i], "-s", 2)) {
	m->style = getstyle(argv[i+1]);
    } else {
	break;
    }
  }

  fname = argv[i];
  fullname = findfile( infname, fname );
  if(fullname == NULL || (inf = fopen(fullname, "r")) == NULL) {
    msg("waveobj: can't find .obj file %s", fname);
    return;
  }

#define VINIT(what) c##what = NULL, k##what = n##what = 0
#define VTOTAL(what) (k##what + n##what)
#define VNEXT(what) \
    if(++k##what >= CHUNK) { \
	struct chain *ct = (struct chain *)malloc(sizeof(*ct)); \
	ct->data = malloc(sizeof(v##what)); \
	memcpy(ct->data, v##what, sizeof(v##what)); \
	ct->pos = n##what; \
	ct->next = c##what; \
	c##what = ct; \
	k##what = 0; \
	n##what += CHUNK; \
    }
#define VSTUFF(dest, what) { \
	struct chain *ct, *cn; \
	memcpy( &dest[n##what], v##what, k##what * sizeof(v##what[0]) );  \
	for(ct = c##what; ct != NULL; ct = cn) {			  \
	    cn = ct->next;	\
	    memcpy( &dest[ct->pos], ct->data, CHUNK*sizeof(v##what[0]) ); \
	    free(ct->data);	\
	    free(ct);		\
	}			\
    }

  VINIT(pts);
  VINIT(fvs);
  VINIT(fv0);
  VINIT(fvn);
  lno = 0;
  while(fgets(line, LINEBUFSIZE, inf) != NULL) {
    char *word, *s = line;
    int slen;
    lno++;
    while(isspace(*s)) s++;
    if(*s == '#' || *s == '\0') continue;
    word = s++;
    while(*s != '\0' && !isspace(*s)) s++;
    slen = s - word;
    if(slen == 1 && word[0] == 'v') {
	Point *p = &vpts[kpts];
	char *ep;
	p->x[0] = strtod(s, &s);
	p->x[1] = strtod(s, &s);
	p->x[2] = strtod(s, &ep);
	if(s == ep) {
	    msg("waveobj: %s line %d: bad v line %s", fullname, lno, line);
	    continue;
	}
	VNEXT(pts);

    } else if(slen == 1 && word[0] == 'f') {
	char *ep;
	int myfv0 = VTOTAL( fvs );
	int myfvn = 0;

	for(;;) {
	    while(isspace(*s)) s++;
	    if(*s == '\0') break;
	    vfvs[kfvs] = strtol(s, &ep, 10) - 1;
	    if(s == ep) {
		msg("waveobj: %s line %d: bad f line %s", fullname, lno, line);
		myfvn = 0;
		break;
	    }
	    s = ep;
	    while(!isspace(*s)) s++;
	    VNEXT(fvs);
	    myfvn++;
	}
	vfv0[kfv0] = myfv0;
	vfvn[kfvn] = myfvn;
	if(myfvn >= 3) {
	    VNEXT(fv0);
	    VNEXT(fvn);
	}
    }
  }
  fclose(inf);
  m->pts = NewN( Point, VTOTAL(pts) );
  VSTUFF( m->pts, pts );
  m->fv0 = NewN( int, VTOTAL(fv0) );
  VSTUFF( m->fv0, fv0 );
  m->fvn = NewN( int, VTOTAL(fvn) );
  VSTUFF( m->fvn, fvn );
  m->fvs = NewN( int, VTOTAL(fvs) );
  VSTUFF( m->fvs, fvs );
  m->nfaces = VTOTAL(fvn);
  m->nfv = VTOTAL(fvs);
  m->nverts = VTOTAL(pts);
  for(i = m->nfv; --i >= 0; ) {
    if(m->fvs[i] < 0 || m->fvs[i] >= m->nverts) {
	fprintf(stderr, "%s: vert index %d out of range 1..%d\n",
		fullname, m->fvs[i]+1, m->nverts);
	m->fvs[i] = 0;
    }
  }
  m->fnorms = NewN( Point, m->nfaces );
  for(f = 0; f < m->nfaces; f++) {
    Point vab, vac, normal;
    int myfv0 = m->fv0[f];
    if(m->fvn[f] > 3 && m->fvs[myfv0] == m->fvs[myfv0+1])
	myfv0++;		/* degenerate quad? */
    vsub( &vab, &m->pts[ m->fvs[myfv0+1] ], &m->pts[ m->fvs[myfv0] ] );
    vsub( &vac, &m->pts[ m->fvs[myfv0+2] ], &m->pts[ m->fvs[myfv0] ] );
    vcross( &normal, &vab, &vac );
    vunit( &m->fnorms[f], &normal );
  }

  if(timestep >= 0) {
    if(timestep >= st->ntimes)
	specks_timespecksptr( st, st->curdata, timestep );
    m->next = st->meshes[st->curdata][timestep];
    st->meshes[st->curdata][timestep] = m;
  } else {
    m->next = st->staticmeshes;
    st->staticmeshes = m;
  }
teuben's avatar
teuben committed
void specks_read( struct stuff **stp, char *fname )
{
  FILE *f;
  char line[LINEBUFSIZE], oline[LINEBUFSIZE], *tcp;
teuben's avatar
teuben committed
  struct stuff *st = *stp;
  int maxfields = 0;

slevy's avatar
 
slevy committed
#define SPECKCHUNK 2001	/* should be bigger, but too many machines have tiny stack areas! */
#define MAXARGS 48
  int argc;
  char *argv[MAXARGS+1];
  struct speck speckbuf[SPECKCHUNK];
  struct speck *sp;
teuben's avatar
teuben committed
  struct speck s;
  struct specklist tsl;
  int i, nsp, maxnsp;
teuben's avatar
teuben committed
  int ignorefirst = 0;
  int lno = 0;
teuben's avatar
teuben committed

  if(fname == NULL) return;

  tcp = NewA( char, strlen(fname)+1 );	/* fname's space might get reused */
  strcpy(tcp, fname);
  fname = tcp;
teuben's avatar
teuben committed

  if((f = fopen(fname, "rb")) == NULL) {
teuben's avatar
teuben committed
    msg("%s: can't open: %s", fname, strerror(errno));
    return;
  }

  tsl.bytesperspeck = (st->maxcomment+1 +
			(sizeof(s) - sizeof(s.title)) + 3) & ~3;
teuben's avatar
teuben committed
  s.size = 1;
  nsp = 0;
  sp = speckbuf;
  maxnsp = sizeof(speckbuf) / tsl.bytesperspeck;
teuben's avatar
teuben committed

#define SPFLUSH() \
	addchunk( st, nsp, tsl.bytesperspeck,		\
		speckscale, speckbuf, NULL,		\
		maxfields >= MAXVAL ? tsl.bytesperspeck \
			: SMALLSPECKSIZE(maxfields) );	\
    }							\
    nsp = maxfields = 0;				\
teuben's avatar
teuben committed

  line[sizeof(line)-1] = '\1';
  while( fgets(line, sizeof(line), f) != NULL ) {
    lno++;
    if(line[sizeof(line)-1] != 1) {
	if(line[sizeof(line)-2] != '\n') {
	    while((i = getc(f)) != EOF && i != '\n')
		;
	}
	line[sizeof(line)-1] = '\1';
	line[sizeof(line)-2] = '\0';
	msg("%s line %d: truncated (>%d chars long!)",
		fname, lno, sizeof(line)-2);
teuben's avatar
teuben committed
    }

    argc = tokenize(line, oline, MAXARGS, argv, &comment);
teuben's avatar
teuben committed

    while(argc > 0 && !strcmp(argv[0], "add")) {
	for(i = 1; i <= argc; i++) argv[i-1] = argv[i];
	argc--;
    }
teuben's avatar
teuben committed
	continue;

    if(nsp >= maxnsp || (isalnum(argv[0][0]) && !isdigit(argv[0][0]))) {
teuben's avatar
teuben committed
	SPFLUSH();
    }

    if(!strcmp(argv[0], "include") || !strcmp(argv[0], "read")) {
teuben's avatar
teuben committed
	float oldscale = speckscale;
	char *infname = argv[1];
teuben's avatar
teuben committed
	char *realfile = findfile( fname, infname );
	if(realfile == NULL) {
	    msg("%s: Can't find include-file %s", fname, infname);
	} else {
	    specks_read( &st, realfile );
	}
	speckscale = oldscale;

    } else if(!strcmp(argv[0], "object") && argc>1) {
teuben's avatar
teuben committed

	char *eq = strchr(argv[1], '=');

	i = 1;
	parti_object( argv[1], &st, 1 );
	if(eq) {
	    parti_set_alias( st, eq+1 );
	} else if(argc >= 3 && !strcmp(argv[2], "=")) {
	    /* object g3 = alias */
	    parti_set_alias( st, argv[3] );
teuben's avatar
teuben committed

    } else if(parti_read(stp, argc, argv, fname)) {
	st = *stp;	/* In case st changed */
    } else if(!strcmp(argv[0], "sdb") && argc>1) {
teuben's avatar
teuben committed
	int tno = st->datatime;
	char *realfile;
	i = 1;
	if(argc>3 && !strcmp(argv[1], "-t")) {
	    if((tno = getfloat(argv[2], st->datatime)) < 0) {
		msg("sdb -t: expected timestepnumber(0-based), not %s", argv[2]);
teuben's avatar
teuben committed
		continue;
	    }
teuben's avatar
teuben committed
	}
	realfile = findfile( fname, argv[i] );
teuben's avatar
teuben committed
	if(realfile == NULL) {
	    msg("%s: sdb: can't find file %s", fname, argv[i]);
teuben's avatar
teuben committed
	} else {
	    specks_read_sdb( st, realfile, tno );
	}

    } else if(!strcmp(argv[0], "sdbvars")) {
	if(argc > 1) {
	    if(argv[1][strspn(argv[1], "mMcrogtxyzSn")] != '\0') {
teuben's avatar
teuben committed
		msg("sdbvars: pattern must contain only chars from: mMcrogtxyzSn, not %s",
teuben's avatar
teuben committed
	    } else {
		if(st->sdbvars) Free(st->sdbvars);
		st->sdbvars = shmstrdup(argv[1]);
teuben's avatar
teuben committed
	    }
	} else {
	    msg("sdbvars %s", st->sdbvars);
	}

    } else if(!strcmp(argv[0], "box") || !strcmp(argv[0], "boxes")) {
teuben's avatar
teuben committed
	Point cen, rad;
	struct AMRbox box;
teuben's avatar
teuben committed

	int tno = -1;

	box.boxno = -1;
	box.level = 0;

	i = 1;
	while(i+2 < argc && argv[i][0] == '-') {
	    if(argv[i][1] == 'n' && sscanf(argv[i+1], "%d", &box.boxno)>0)
		i += 2;
	    else if(argv[i][1] == 'l' && sscanf(argv[i+1], "%d", &box.level)>0)
		i += 2;
	    else if(argv[i][1] == 't' && (tno = getfloat(argv[i+1], st->datatime)) >= 0)
		i += 2;
teuben's avatar
teuben committed
	    else
		break;
	}
	if(i+2 == argc &&
	     3==sscanf(argv[i], "%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2]) &&
	     0<(k=sscanf(argv[i+1], "%f%*c%f%*c%f", &rad.x[0],&rad.x[1],&rad.x[2]))) {
teuben's avatar
teuben committed
	    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(i+3 == argc &&
		    2==sscanf(argv[i], "%f%*c%f",&box.p0.x[0],&box.p1.x[0]) &&
		    2==sscanf(argv[i+1], "%f%*c%f",&box.p0.x[1],&box.p1.x[1]) &&
		    2==sscanf(argv[i+2], "%f%*c%f",&box.p0.x[2],&box.p1.x[2])) {
teuben's avatar
teuben committed

	    specks_add_box( st, &box, tno );

	} else if(i+6 == argc) {
	    k = getfloats( &box.p0.x[0], 3, i, argc, argv )
	      + getfloats( &box.p1.x[0], 3, i+3, argc, argv );
	    if(k==6)
teuben's avatar
teuben committed
		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(i+1 == argc) {
	    char *realfile = findfile( fname, argv[i] );
teuben's avatar
teuben committed
	    if(realfile == NULL) {