Skip to content
Snippets Groups Projects
partiviewc.cc 25.1 KiB
Newer Older
#include <stdio.h>
#include <stdlib.h>

#ifdef WIN32
# include "winjunk.h"
#endif

#include <GL/gl.h>
#include "geometry.h"
#include "shmem.h"
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <errno.h>
#include <signal.h>

#if unix
# include <GL/glx.h>		/* for try_stereo() */
# include <FL/x.H>
#endif

#include "specks.h"
#include "partiview.H"
#include "partiviewc.h"

#include <ctype.h>
#undef isspace		/* hack for irix 6.5 */

extern "C" {

/* Handle pick results */
slevy's avatar
slevy committed
void specks_picker( Fl_Gl_Window *junk, int nhits, int nents, GLuint *hitbuf, void *vview )
{
  Fl_Gview *view = (Fl_Gview *)vview;
  struct stuff *st;
  unsigned int bestz = ~0u;
  struct stuff *bestst = NULL;
  struct specklist *bestsl;
  int bestspeckno;
  int id, bestid = -1;
  Point bestpos, wpos, cpos;
  int centerit = Fl::event_state(FL_SHIFT);

  for(id = 0; id < MAXSTUFF; id++) {
    if((st = stuffs[id]) == NULL) continue;
    if(specks_partial_pick_decode( st, id, nhits, nents, hitbuf,
					&bestz, &bestsl, &bestspeckno, &bestpos )) {
	bestst = st;
	bestid = id;
    }
  }
  if(bestst) {
    char fmt[1024], wpostr[80];
    vtfmpoint( &wpos, &bestpos, view->To2w( bestid ) );
    if(memcmp( &wpos, &bestpos, sizeof(wpos) )) {
	sprintf(wpostr, " (w%g %g %g)", wpos.x[0],wpos.x[1],wpos.x[2]);
    } else {
	wpostr[0] = '\0';
    }
    vtfmpoint( &cpos, &wpos, view->Tw2c() );
    strcpy(fmt, bestsl->bytesperspeck <= SMALLSPECKSIZE(MAXVAL)
		? "[g%d]%sPicked %g %g %g%s%.0s @%g (of %d)"
		: "[g%d]%sPicked %g %g %g%s \"%s\" @%g (of %d)");
    if(bestsl->text == NULL && bestst->vdesc[bestst->curdata][0].name[0] != '\0') {
	strcat(fmt, ";");
	for(int i = 0; i < MAXVAL && bestst->vdesc[bestst->curdata][i].name[0]; i++)
	    sprintf(fmt+strlen(fmt), " %.9s %%g", bestst->vdesc[bestst->curdata][i].name);
    }
    if(bestsl->text != NULL) {
	sprintf(fmt+strlen(fmt), " \"%s\"", bestsl->text);
    }
    struct speck *sp = NextSpeck( bestsl->specks, bestsl, bestspeckno);
    msg(fmt, bestid, centerit?"*":"",
		bestpos.x[0],bestpos.x[1],bestpos.x[2], wpostr,
	bestsl->text ? bestsl->text : sp->title,
	vlength( &cpos ), nhits,
	sp->val[0],sp->val[1],sp->val[2],sp->val[3],sp->val[4],
	sp->val[5],sp->val[6],sp->val[7],sp->val[8],sp->val[9] );

    if(centerit) {
slevy's avatar
slevy committed
	view->center( &wpos );
	if(ppui.censize > 0) view->redraw();
    }
  } else {
    msg("Picked nothing (%d hits)", nhits);
  }
}

void draw_specks( Fl_Gview *view, void *vst, void *junk ) {
  struct stuff *st = (struct stuff *)vst;
slevy's avatar
 
slevy committed
  specks_set_timestep( st );
  specks_current_frame( st, st->sl );
  st->inpick = view->inpick();
  drawspecks( st );
  st->inpick = 0;
}

void parti_allobjs( int argc, char *argv[], int verbose ) {
slevy's avatar
 
slevy committed
  for(int i = 0; i < MAXSTUFF; i++) {
    struct stuff *st = stuffs[i];
    if(st) {
	if(verbose) msg(st->alias ? "g%d=%s:" : "g%d:", i, st->alias);
slevy's avatar
 
slevy committed
	specks_parse_args( &st, argc, argv );
slevy's avatar
 
slevy committed
  }
}

void parti_redraw() {
  if(ppui.view) ppui.view->redraw();
  if(ppui.hrdiag->visible_r()) ppui.hrdiag->redraw();
}

void parti_update() {
  if(ppui.view)
    while(ppui.view->damage())
	Fl::wait(.1);
}

void parti_censize( float newsize ) {
  if(newsize != ppui.censize) ppui.view->redraw();
  ppui.censize = newsize;
}

float parti_getcensize() {
  return ppui.censize;
}

char *parti_bgcolor( const char *rgb ) {
  static char bgc[16];
  Point bgcolor;

  bgcolor = *ppui.view->bgcolor();
  if(rgb) {
    if(1 == sscanf(rgb, "%f%*c%f%*c%f", &bgcolor.x[0], &bgcolor.x[1], &bgcolor.x[2]))
	bgcolor.x[1] = bgcolor.x[2] = bgcolor.x[0];
    ppui.view->bgcolor( &bgcolor );
  }

  sprintf(bgc, "%.3f %.3f %.3f", bgcolor.x[0],bgcolor.x[1],bgcolor.x[2]);
  return bgc;
}

int *try_quad_stereo()
{
#if unix
  static int attrs[] = {
#ifdef GLX_SAMPLE_BUFFERS_SGIS
    GLX_SAMPLE_BUFFERS_SGIS, 1,
    GLX_SAMPLES_SGIS, 4,
#endif
    GLX_STEREO,
    GLX_RGBA,
    GLX_DOUBLEBUFFER,
    GLX_RED_SIZE, 1,
    GLX_GREEN_SIZE, 1,
    GLX_BLUE_SIZE, 1,
    GLX_DEPTH_SIZE, 1,
    None
  };
  int *ap = attrs;

#ifdef GLX_SAMPLE_BUFFERS_SGIS
  if(Fl_Gl_Window::can_do( ap )) return ap;
  ap += 4;
#endif

  /* When our view window(s) are created, make sure they ask for the
   * same set of values.
   */
  if(Fl_Gl_Window::can_do( ap ))
    return ap;
  return NULL;

#else  /* win32 */

  return NULL;
#endif
}

char *parti_stereo( const char *ster )
{
  static char rslt[28];
  static enum Gv_Stereo stereotype = GV_MONO;
  static int *quadmode = NULL;
  static char *sternames[] = { "mono", "redcyan", "crosseyed", "glasses" };
  int smode = -1;

  if(stereotype == GV_MONO) {
    quadmode = try_quad_stereo();
    stereotype = (quadmode!=NULL) ? GV_QUADBUFFERED : GV_REDCYAN;
  }

  if(ster) {
    float v;
    if(sscanf(ster, "%f", &v)) {
	ppui.view->stereosep(v);
	smode = stereotype;
    }
    if(strstr(ster,"on"))
	smode = stereotype;
    if(strstr(ster,"off"))
	smode = GV_MONO;
    if(strstr(ster, "red"))
	smode = stereotype = GV_REDCYAN;
    if(strstr(ster, "quad") || strstr(ster, "gla") ||
				strstr(ster, "hard") || strstr(ster, "hw")) {
	if(quadmode != NULL) {
	    smode = stereotype = GV_QUADBUFFERED;
	} else {
	    msg("Can't do quadbuffered/hardware stereo");
	    smode = GV_MONO;
	}
    }
    if(smode < 0) {
	msg("stereo: [on|off|redcyan|glasses] [stereosep]");
	msg(" Try stereosep values from 0.02 to 0.1, or -.02 .. -.1 to swap eyes");
    }
  }

  if(smode >= 0) {
    if(smode == (int)GV_QUADBUFFERED)
	ppui.view->mode( quadmode );
    else
	ppui.view->mode( FL_RGB | FL_DOUBLE | FL_DEPTH | FL_MULTISAMPLE );
    ppui.view->stereo( (enum Gv_Stereo)smode );
  }

  smode = ppui.view->stereo();
  sprintf(rslt, "%g %s",
	ppui.view->stereosep(),
	(smode<=0||smode>=COUNT(sternames)) ? "off" : sternames[smode]);
  return rslt;
}

char *parti_winsize( CONST char *newsize ) {
  static char cursize[12];
  int ox = ppui.view->w(), oy = ppui.view->h();
  if(!ppui.mainwin->shown()) {
    ox = oy = -1;
  }
    int nx = ox, ny = oy;
    if(sscanf(newsize, "%d%*c%d", &nx, &ny) == 1)
	ny = nx * oy / ox;
    if(ox < 0) {		/* Not yet available -- remember for later */
	strncpy(cursize, newsize, sizeof(cursize)-1);
	ppui.reqwinsize = cursize;
	return cursize;
    }
    if(nx != ox || ny != oy) {
	ppui.mainwin->size( ppui.mainwin->w() + nx - ox,
			    ppui.mainwin->h() + ny - oy );
	parti_update();
	return parti_winsize(NULL);
    }
  }
  sprintf(cursize, "%dx%d", ox, oy);
  return cursize;
}

char *parti_clip( const char *nearclip, const char *farclip ) {
  float n, f;
slevy's avatar
slevy committed
  int changed = 0;
  if(nearclip != NULL && sscanf(nearclip, "%f", &n) > 0) {
    ppui.view->nearclip( n );
slevy's avatar
slevy committed
    changed = 1;
  }
  if(farclip != NULL && sscanf(farclip, "%f", &f) > 0) {
    ppui.view->farclip( f );
slevy's avatar
slevy committed
    changed = 1;
  }
  if(changed && ppui.subcam != 0)
    parti_select_subcam( ppui.subcam );	/* recompute subcam projection */

  static char why[16];
  sprintf(why, "clip %.4g %.4g", ppui.view->nearclip(), ppui.view->farclip());
  return why;
}

int parti_move( const char *onoffobj, struct stuff **newst ) {
  int objno = -1;
  if(onoffobj) {
    if(!strcmp(onoffobj, "off"))
	ppui.view->movingtarget(0);
    else if(!strcmp(onoffobj, "on"))
	ppui.view->movingtarget(1);
    else if(sscanf(onoffobj, "g%d", &objno) > 0 ||
			sscanf(onoffobj, "%d", &objno) > 0) {
	ppui.view->movingtarget(1);
	objno = parti_object( onoffobj, newst, 0 );
    } else {
	msg("move {on|off|g<N>} -- select whether navigation can move sub-objects");
    }
  }
  return ppui.view->movingtarget() ? ppui.view->target() : -1;
}

slevy's avatar
slevy committed
float parti_pickrange( char *newrange ) {
  if(newrange) {
    if(sscanf(newrange, "%f", &ppui.pickrange))
	ppui.view->picksize( 4*ppui.pickrange, 4*ppui.pickrange );
  }

  return ppui.pickrange;
}

static int endswith(char *str, char *suf) {
  if(str==NULL || suf==NULL) return 0;
  int len = strlen(str);
  int suflen = strlen(suf);
  return suflen <= len && memcmp(suf, str+len-suflen, suflen)==0;
}

int parti_snapset( char *fname, char *frameno, char *imgsize )
{
  int len;
  static char suf[] = ".%03d.ppm.gz";
#else
  static char suf[] = ".%03d.ppm";
#endif
  int needsuf;

  if(fname) {
    len = strlen(fname);
    needsuf = strchr(fname, '%')!=NULL || fname[0] == '|'
		  || endswith(fname, ".tif") || endswith(fname, ".tiff")
		  || endswith(fname, ".sgi") || endswith(fname, ".png")
		  || endswith(fname, ".jpeg") || endswith(fname, ".jpg")
		  || endswith(fname, ".ppm") || endswith(fname, ".gz")
		? 0 : sizeof(suf)-1;
    if(ppui.snapfmt)  Free(ppui.snapfmt);
    ppui.snapfmt = NewN(char, len+needsuf+1);
    sprintf(ppui.snapfmt, needsuf ? "%s%s" : "%s", fname, suf);
  }
  if(frameno)
    sscanf(frameno, "%d", &ppui.snapfno);
  return ppui.snapfno;
}

int parti_snapshot( char *snapinfo )
{
  char tfcmd[10240], *tftail;
  int fail;
#if !WIN32  /* if unix */
  static char defsnap[] = "snap.%03d.sgi";
  static char prefix[] = "|convert ppm:- ";
  static char gzprefix[] = "|gzip >";
#else
  static char defsnap[] = "snap.%03d.ppm";
  static char prefix[] = "";
#endif

  if(snapinfo) snapinfo[0] = '\0';
  if(ppui.snapfmt == NULL) {
    ppui.snapfmt = NewN(char, sizeof(defsnap)+1);
    strcpy(ppui.snapfmt, defsnap);
  }
  
  if(ppui.snapfmt[0] == '|' || endswith(ppui.snapfmt, ".ppm")) {
    tfcmd[0] = '\0';
#if unix
  } else if(endswith(ppui.snapfmt, ".ppm.gz")) {
    strcpy(tfcmd, gzprefix);
    strcpy(tfcmd, prefix);
  }
  tftail = tfcmd+strlen(tfcmd);
  sprintf(tftail, ppui.snapfmt, ppui.snapfno);
  if(!ppui.view || !ppui.view->visible_r()) {
    msg("snapshot: no visible graphics window?");
    return -2;
  }

  Fl_Widget *pa;
  for(pa = ppui.view; pa->parent(); pa = pa->parent()) ;
  pa->show();	// raise window
  // Ensure window's image is up-to-date
  parti_update();

  int y, h = ppui.view->h(), w = ppui.view->w();
  char *buf = (char *)malloc(w*h*3);
  if(!ppui.view->snapshot( 0, 0, w, h, buf )) {
    free(buf);
    msg("snapshot: couldn't read from graphics window?");
    return -2;
  }

  FILE *p;
#if unix
  void (*oldpipe)(int);
  int popened = tfcmd[0] == '|';
  if(popened) {
    oldpipe = signal(SIGPIPE, SIG_IGN);
    p = popen(tfcmd+1, "w");
  } else
#endif
    p = fopen(tfcmd, "wb");

  fprintf(p, "P6\n%d %d\n255\n", w, h);
  for(y = h; --y >= 0 && fwrite(&buf[w*3*y], w*3, 1, p) > 0; )
    ;
  free(buf);
  fflush(p);
  fail = ferror(p);

#if unix
  if(popened) {
    pclose(p);
    signal(SIGPIPE, oldpipe);
  }
  else
#endif
  if(y >= 0 || fail) {
    msg("snapshot: Error writing to %s", tfcmd);
    return -1;
  }
  if(snapinfo)
    sprintf(snapinfo, "%.1000s [%dx%d]", tftail, w, h);
  return ppui.snapfno++;
}

const char *parti_get_alias( struct stuff *st ) {
  return st && st->alias ? st->alias : "";
}

/*
 * This is how new objects get created -- via "object g<N>" command.
 */
int parti_object( const char *objname, struct stuff **newst, int create ) {
  int i = -1;

  if(newst)
    *newst = ppui.st;

  if(objname == NULL) {
    for(i = 1; i < MAXSTUFF; i++)
	if(ppui.st == stuffs[i]) return i;
    return -1;
  }
  if(ppui.view == NULL)
    return -1;
  if(!strcmp(objname, "c") || !strcmp(objname, "c0")) {
    ppui.view->target( GV_ID_CAMERA );
    return -1;
  }
  c = '=';
  if((		/* accept gN or gN=... or N or N=... or [gN] */
	((sscanf(objname, "g%d%c", &i, &c) > 0 || sscanf(objname, "%d%c", &i, &c) > 0) && c == '=')
	|| sscanf(objname, "[g%d]", &i) > 0)) {
  } else {
    for(i = MAXSTUFF; --i >= 0; ) {
	if(stuffs[i] && stuffs[i]->alias && !strcmp(stuffs[i]->alias, objname))
	    break;
    }
  }
  if(i == -1 && create && !gname) {
    for(i = 1; i < MAXSTUFF && stuffs[i]; i++)
	;
  }
  if(i >= MAXSTUFF) {
    msg("Oops, can only have objects g1..g%d", MAXSTUFF-1);
    return -1;
  }
  if(i > 0) {
    if(stuffs[i] == NULL) {
	stuffs[i] = specks_init( 0, NULL );
slevy's avatar
 
slevy committed
	stuffs[i]->clk = ppui.clk;
	ppui.view->add_drawer( draw_specks, stuffs[i], NULL, NULL, i );
	ppui.view->picker( specks_picker, ppui.view );
slevy's avatar
slevy committed
	ppui.view->picksize( 4*ppui.pickrange, 4*ppui.pickrange );
	char tname[12];
	sprintf(tname, "[g%d]", i);
	int me = ppui.obj->add( strdup(tname) );

	((Fl_Menu_Item *)ppui.obj->menu())[me].labelcolor( ppui.obj->labelcolor() );
	if(!gname)
	    parti_set_alias(stuffs[i], (char *)objname);
    }
    ppui.st = stuffs[i];
    ppui.view->target( i );
    ppui_refresh( ppui.st );
    if(newst) *newst = stuffs[i];
    return i;
  }
  msg("Don't understand object name \"%s\"", objname);
  return -1;
}

int parti_idof( struct stuff *st ) {
  for(int i = 0; i < MAXSTUFF; i++)
    if(stuffs[i] == st) return i;
  return -1;
}

float parti_focal( CONST char *newfocal ) {
  float focallen;
  if(newfocal && sscanf(newfocal, "%f", &focallen) > 0)
    ppui.view->focallen( focallen );
  return ppui.view->focallen();
}

float parti_fovy( CONST char *newfovy ) {
  float fovy;
  if(newfovy && sscanf(newfovy, "%f", &fovy) > 0)
    if(ppui.view->perspective())
	ppui.view->angyfov( fovy );
    else
	ppui.view->halfyfov( 2*fovy );

  return ppui.view->perspective() ? ppui.view->angyfov()
				  : 2 * ppui.view->halfyfov();
}
    

void parti_getc2w( Matrix *c2w ) {
  if(ppui.view && c2w)
    *c2w = *ppui.view->Tc2w();
}

void parti_setc2w( const Matrix *c2w ) {
  ppui.view->Tc2w( c2w );
}

void parti_seto2w( struct stuff *, int objno, const Matrix *o2w ) {
  ppui.view->To2w( objno, o2w );
}

void parti_geto2w( struct stuff *, int objno, Matrix *o2w ) {
  *o2w = *ppui.view->To2w( objno );
}

void parti_parent( struct stuff *st, int parent ) {
  ppui.view->objparent( parti_idof(st), parent );
}

slevy's avatar
 
slevy committed
void parti_nudge_camera( Point *disp ) {
  Matrix Tc2w, Tdisp, Tnewc2w;

  if(ppui.view) {
    Tc2w = *ppui.view->Tc2w();
    mtranslation( &Tdisp, disp->x[0], disp->x[1], disp->x[2] );
    mmmul( &Tnewc2w, &Tc2w,&Tdisp );
    ppui.view->Tc2w( &Tnewc2w );
  }
}

void subcam_matrices( Subcam *sc, Matrix *Tc2subc, float subclrbt[4] )
slevy's avatar
slevy committed
{
  Matrix Ry, Rx, Rz, Rflop, Tfy, Tfyx;
  mrotation( &Rflop, 90,     'x' );
  mrotation( &Ry, -sc->azim, 'y' );
  mrotation( &Rx, -sc->elev, 'x' );
  mrotation( &Rz, -sc->roll, 'z' );
  mmmul( &Tfy, &Rflop, &Ry );
  mmmul( &Tfyx, &Tfy, &Rx );
  mmmul( Tc2subc, &Tfyx, &Rz );

  subclrbt[0] =  tanf( sc->nleft * (M_PI/180) );
  subclrbt[1] = -tanf( sc->right * (M_PI/180) );
  subclrbt[2] =  tanf( sc->ndown * (M_PI/180) );
  subclrbt[3] = -tanf( sc->up    * (M_PI/180) );
slevy's avatar
slevy committed
}

int parti_make_subcam( CONST char *name, int argc, CONST char **params )
{
  Subcam tsc;
  int index;
  int i, any = 0;
  if(name == NULL || params == NULL)
    return 0;

  index = parti_subcam_named( name );
  if(index > 0)
    tsc = ppui.sc[index-1];

  for(i = 0; i < 7 && i < argc; i++) {
    char junk;
    int ok = sscanf(params[i], "%f%c", ((float *)&tsc.azim) + i, &junk);
    if(ok == 1) any++;
    else if(0!=strcmp(params[i], ".") && 0!=strcmp(params[i], "-")) {
	msg("subcam %s: what's ``%s''?", name, params[i]);
	return 0;
    }
  }

  if(tsc.nleft + tsc.right == 0 || tsc.ndown + tsc.up == 0) {
    msg("subcam %s -- degenerate?", name);
    return 0;
  }
  if(any == 0)
    return index;

  if(index == 0) {
    if(any != 7) {
	msg("new subcam %s: expected 7 parameters", name);
	return 0;
    }
    for(i = 0; ppui.sc[i].name[0] != '\0'; i++) {
	if(i >= MAXSUBCAM-1) {
	    msg("Too many subcams (MAXSUBCAM=%d) -- ignoring new %s",
		MAXSUBCAM, name);
	    return 0;
	}
    }
    index = i;
  }

  sprintf(tsc.name, "%.7s", name);
  ppui.sc[index] = tsc;
  return index+1;
}

int parti_subcam_named( CONST char *name )
{
  for(int i = 0; i < MAXSUBCAM; i++)
    if(!strncmp(ppui.sc[i].name, name, 7))
	return i+1;
  return 0;
}

int parti_select_subcam( int index )
{
  if(index < 0 || index > MAXSUBCAM)
    return ppui.subcam;

  ppui.subcam = index;
  if(index == 0) {
    ppui.view->usesubcam(0);
    parti_redraw();
    return 0;
  }

  Subcam *tsc = &ppui.sc[index-1];

  Matrix Tc2subc;
  float subclrbt[4];
  subcam_matrices( tsc, &Tc2subc, subclrbt );
slevy's avatar
slevy committed
  ppui.view->Tc2subc( &Tc2subc );
  ppui.view->subc_lrbt( subclrbt );
slevy's avatar
slevy committed
  ppui.view->usesubcam(1);
  parti_redraw();
  return index;
}

char *parti_get_subcam( int index, char *paramsp )
{
  if(paramsp) paramsp[0] = '\0';
  if(index == 0 || index > MAXSUBCAM) return "";
  Subcam *sc = &ppui.sc[index-1];
  if(sc->name[0] == '\0') return "";
  if(paramsp)
    sprintf(paramsp, "%g %g %g %g %g %g %g",
	sc->azim, sc->elev, sc->roll,
	sc->nleft, sc->right, sc->ndown, sc->up);
  return sc->name;
}

int parti_current_subcam( void )
{
  return ppui.subcam;
}

char *parti_subcam_list()
{
  static char what[MAXSUBCAM*9], *tail;
  static char blank[2] = "";
slevy's avatar
slevy committed
  what[0] = '\0';
  int i;
  for(i = 0, tail = what; i < MAXSUBCAM; i++) {
    if(ppui.sc[i].name[0] != '\0') {
	sprintf(tail, " %s", ppui.sc[i].name);
	tail += strlen(tail);
    }
  }
  return((tail==what) ? blank : what+1);
slevy's avatar
slevy committed
}

struct Fl_Plot *parti_register_plot( struct stuff *st, void (*draw)(struct Fl_Plot *, void *obj, void *arg), void *arg ) {
slevy's avatar
slevy committed
    if(ppui.hrdiag) {
	ppui.hrdiag->add_drawer( draw, st, arg, parti_get_alias(st), parti_idof(st) );
	ppui.hrdiag->picker( specks_picker, ppui.view );
	ppui.hrdiag->picksize( 2*ppui.pickrange, 2*ppui.pickrange );
    }
    return ppui.hrdiag;
void parti_setpath( struct stuff *st, int nframes, struct wfframe *frames, float fps = 30 ) {
  struct wfpath *path = &ppui.path;
  if(path->frames != NULL)
    free(path->frames);
  path->frames = NULL;
  path->fps = (fps <= 0 ? 30.0 : fps);
  path->nframes = nframes;
  path->frame0 = 1;
  path->curframe = path->frame0;
  if(nframes > 0) {
    path->frames = NewN( struct wfframe, nframes );
    memcpy( path->frames, frames, nframes*sizeof(struct wfframe) );
  }

  if(ppui.playframe) {
    ppui.playframe->minimum( path->frame0 );
    ppui.playframe->maximum( path->frame0 + path->nframes - 1 );
    ppui.playframe->value( path->frame0 );

    ppui.playtime->step( 1, 100 );
    ppui.playtime->minimum( 0.0 );
    ppui.playtime->maximum( path->nframes / path->fps );
    ppui.playtime->value( 0.0 );
  }
}

int parti_readpath( struct stuff *st, const char *fname ) {
  int nframes, room;
  struct wfframe *fr, *cf;
  FILE *f;
  char line[256], *cp;
  int lno = 0;

  if(fname == NULL || (f = fopen(fname, "r")) == NULL) {
    msg("readpath: %.200s: cannot open: %s", fname, strerror(errno));
    return 0;
  }

  nframes = 0;
  room = 2000;
  fr = NewA( struct wfframe, room );
  while(fgets(line, sizeof(line), f) != NULL) {
    lno++;
    for(cp = line; isspace(*cp); cp++)
	;
    if(*cp == '#' || *cp == '\0') continue;
    if(nframes >= room) {
	struct wfframe *nfr;
	room *= 3;
	nfr = NewN( struct wfframe, room );
	memcpy( nfr, fr, nframes * sizeof(struct wfframe) );
	if(!first) Free(fr);
	first = 0;
	fr = nfr;
    }
    cf = &fr[nframes];
    if(sscanf(cp, "%f%f%f%f%f%f%f",
	    &cf->tx,&cf->ty,&cf->tz, &cf->rx,&cf->ry,&cf->rz, &cf->fovy) < 7) {
	msg( "readpath: %.200s line %d: expected tx ty tz rx ry rz fovy, got: %.100s",
	    fname, lno, line);
	fclose(f);
	return 0;
    }
    nframes++;
  }
  fclose(f);
  parti_setpath( st, nframes, fr );
  if(!first) Free(fr);
  parti_setframe( st, 1 );
  msg("%d frames in %.200s", nframes, fname);
  return 1;
}

static void fd_got_data( int fd, void * ) {
  specks_check_async( &ppui.st );
}

void parti_asyncfd(int fd) { Fl::add_fd( fd, fd_got_data ); }
void parti_unasyncfd(int fd) { Fl::remove_fd(fd); }

static void playidle( void * ) {
  struct stuff *st = ppui.st;
  struct wfpath *path = &ppui.path;

  if(!ppui.playing) {
    Fl::remove_idle( playidle, st );
    ppui.playidling = 0;
    return;
  }

slevy's avatar
 
slevy committed
  double now = wallclock_time();
  
  if(ppui.playspeed == 0)
    ppui.playspeed = 1;

  int frame = ppui.framebase;

  if(ppui.playevery) {
    frame += (int) ((ppui.playspeed < 0)	/* round away from zero */
		    ? ppui.playspeed - .999f
		    : ppui.playspeed + .999f);
    ppui.framebase = frame;
slevy's avatar
 
slevy committed
    ppui.playtimebase = now;
  } else {
slevy's avatar
 
slevy committed
    frame += (int) ((now - ppui.playtimebase) * ppui.playspeed * path->fps);
  }

  if(frame < path->frame0 || frame >= path->frame0 + path->nframes) {

    frame = (frame < path->frame0)
	  ? path->frame0
	  : path->frame0 + path->nframes-1;

    switch(ppui.playloop) {
    case 0:	/* Not looping; just stop at end. */
	Fl::remove_idle( playidle, st );
	ppui.playidling = 0;
	parti_play( st, "0" );
	break;

    case -1:	/* Bounce: stick at same end of range, change direction */
	ppui.playspeed = -ppui.playspeed;
	break;

    case 1:	/* Loop: jump to other end of range */
	frame = (frame == path->frame0)
		? path->frame0 + path->nframes-1
		: path->frame0;
    }
    ppui.framebase = frame;
slevy's avatar
 
slevy committed
    ppui.playtimebase = now;
    
    parti_setframe( st, frame );
    return;
  }

  /*
   * Cases:
   *  - we're displaying frames slower than real time.
   *	=> use an idle function.
   *  - we're displaying frames faster than real time.
   *    => use a timeout.
   */

  int needidle = ppui.playidling;
  float dt = 0;

  if(frame != path->curframe) {
    parti_setframe( st, frame );
    needidle = 1;
  } else {
    frame += (ppui.playspeed > 0) ? 1 : -1;
    dt = (frame - ppui.framebase) / (ppui.playspeed * path->fps)
slevy's avatar
 
slevy committed
	+ ppui.playtimebase - now;
    needidle = (dt < .05);
  }

  if(needidle && !ppui.playidling) {
    Fl::add_idle( playidle, st );
  }
  if(!needidle && ppui.playidling) {
    Fl::remove_idle( playidle, st );
  }
  ppui.playidling = needidle;

  if(!needidle) {
fprintf(stderr, "<%.3f>", dt);
    Fl::add_timeout( dt, playidle, st );
  }
}

void parti_play( struct stuff *st, const char *rate ) {

  struct wfpath *path = &ppui.path;
  int playnow = 1;

  if(rate != NULL) {
    char *ep;
    float sp = strtod(rate, &ep);
    if(strchr(ep, 's')) playnow = 0;
    if(strchr(ep, 'l')) ppui.playloop = 1;
    else if(strchr(ep, 'k')) ppui.playloop = -1; /* rock */
    else if(strchr(ep, 'f') || strchr(ep, 'e')) ppui.playevery = 1;
    else if(strchr(ep, 'r') || strchr(ep, 't')) ppui.playevery = 0;
    if(ep != rate) {
	if(sp == 0) playnow = 0;
	else ppui.playspeed = sp;
    }
  }

  /* Always stop any ongoing timers */
  Fl::remove_idle( playidle, st );
  Fl::remove_timeout( playidle, st );
  ppui.playing = 0;
  ppui.playidling = 0;

  if(playnow) {
slevy's avatar
 
slevy committed
    ppui.playtimebase = wallclock_time();
    ppui.framebase = path->curframe;
    if(ppui.playspeed > 0 && path->curframe >= path->frame0+path->nframes-1)
	ppui.framebase = path->frame0;
    else if(ppui.playspeed < 0 && path->curframe <= path->frame0)
	ppui.framebase = path->frame0 + path->nframes - 1;

    parti_setframe( st, ppui.framebase );
    Fl::add_idle( playidle, st );
    ppui.playing = 1;
    ppui.playidling = 1;
  }

  if(ppui.play != NULL) {
    ppui.play->label( ppui.playing ? "stop" : "play" );
    ppui.play->value( ppui.playing );
    ppui.play->redraw();
  }
}


void wf2c2w( Matrix *Tcam, const struct wfframe *wf )
{
  Matrix Rx, Ry, Rz, T;

  mtranslation( &T, wf->tx, wf->ty, wf->tz );
  mrotation( &Ry,  wf->ry, 'y' );
  mrotation( &Rz, wf->rz, 'z' );
  mrotation( &Rx, wf->rx, 'x' );

  mmmul( Tcam, &Rz, &Rx );
  mmmul( Tcam, Tcam, &Ry );
  mmmul( Tcam, Tcam, &T );	/* so Tcam = Rz * Rx * Ry * T */
}

int parti_frame( struct stuff *st, CONST char *frameno,
			CONST struct wfpath **pp ) {
  if(pp != NULL)
    *pp = &ppui.path;
  if(frameno != NULL)
    return parti_setframe( st, atoi(frameno) );
  return (ppui.path.frames == NULL || ppui.path.nframes <= 0)
	? -1 : ppui.path.curframe;
}


int parti_setframe( struct stuff *st, int fno ) {

  struct wfpath *path = &ppui.path;

  if(path->frames == NULL || path->nframes <= 0)
    return -1;
  if(fno < path->frame0)
    fno = path->frame0;
  else if(fno >= path->frame0 + path->nframes)
    fno = path->frame0 + path->nframes - 1;

  Matrix Tc2w;
  wf2c2w( &Tc2w, &path->frames[fno - path->frame0] );
  ppui.view->Tc2w( &Tc2w );
  ppui.view->angyfov( path->frames[fno - path->frame0].fovy );
  path->curframe = fno;

  if(ppui.playframe) {
    ppui.playframe->value( fno );
    float t = (fno - path->frame0) / path->fps;
    if(fabs(t - ppui.playtime->value()) * path->fps > .5)
	ppui.playtime->value( t );
  }

  return fno - path->frame0;
}

void parti_center( const Point *cen )
{
  if(ppui.view)
	ppui.view->center( cen );
}

void parti_getcenter( Point *cen ) {
  if(ppui.view && cen)
    *cen = *ppui.view->center();
}

slevy's avatar
 
slevy committed

void parti_set_speed( struct stuff *st, double speed ) {
  ppui.stepspeed->truevalue( speed );
}

void parti_set_timebase( struct stuff *st, double timebase ) {
  char str[32];
  ppui.timebasetime = timebase;
  sprintf(str, "%.16lg", timebase);
  ppui.timebase->value( str );
  parti_set_timestep( st, clock_time( st->clk ) );
}

void parti_set_timestep( struct stuff *st, double timestep ) {
  char str[32];
  sprintf(str, "%.16lg", timestep - ppui.timebasetime);
  ppui.timestep->value( str );
  ppui.timestep->redraw();
  ppui.timebase->redraw();
slevy's avatar
 
slevy committed
}

void pp_stepper( void *vst ) {