Skip to content
Snippets Groups Projects
partiviewc.cc 33.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * UI-related glue functions for partiview.
     * Stuart Levy, slevy@ncsa.uiuc.edu
     * National Center for Supercomputing Applications,
     * University of Illinois 2001.
    
     * This file is part of partiview, released under the
     * Illinois Open Source License; see the file LICENSE.partiview for details.
    
    #include <stdio.h>
    #include <stdlib.h>
    
    
    slevy's avatar
    slevy committed
    #ifdef _WIN32
    
    #include "winjunk.h"
    #endif
    
    #ifdef __APPLE__
    #include <OpenGL/gl.h>
    #else
    #include <GL/gl.h>    /* for GLuint */
    
    #endif
    
    #include "geometry.h"
    #include "shmem.h"
    #include <string.h>
    #include <stdarg.h>
    #include <math.h>
    #include <errno.h>
    #include <signal.h>
    
    #include "specks.h"
    #include "partiview.H"
    #include "partiviewc.h"
    
    
    #ifndef FLHACK
    # include <FL/glut.H>	/* for GLUT_STEREO if FLTK knows it */
    #endif
    
    #ifndef GLUT_STEREO
    # if unix
    #  include <FL/x.H>	/* otherwise, need GLX symbols for X-specific stereo */
    #  include <GL/glx.h>
    # endif
    #endif
    
    
    
    #include <ctype.h>
    #undef isspace		/* hack for irix 6.5 */
    
    #undef isdigit
    
    #ifdef FLHACK
    extern void _pview_pickmsg( const char *fmt, ... );
    #endif
    
    
    /* 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)%s"
    		: "[g%d]%sPicked %g %g %g%s \"%s\" @%g (of %d)%s");
        attrs[0] = '\0';
        struct speck *sp = NextSpeck( bestsl->specks, bestsl, bestspeckno);
    
        if(bestsl->text == NULL && bestst->vdesc[bestst->curdata][0].name[0] != '\0') {
    
    	strcpy(attrs, ";");
    	for(int i = 0; i < MAXVAL && bestst->vdesc[bestst->curdata][i].name[0]; i++) {
    	    int pos = strlen(attrs);
    	    snprintf(attrs+pos, sizeof(attrs)-pos, " %s %g",
    		    bestst->vdesc[bestst->curdata][i].name, sp->val[i]);
    	}
    
        if(bestsl->text != NULL) {
    
    	snprintf(attrs+strlen(attrs), sizeof(attrs)-strlen(attrs),
    		" \"%s\"", bestsl->text);
    
    #ifdef FLHACK
        _pview_pickmsg(fmt, bestid, centerit?"*":"",
    	bestpos.x[0],bestpos.x[1],bestpos.x[2], wpostr,
    	bestsl->text ? bestsl->text : sp->title,
    	vlength( &cpos ), nhits,
    	attrs );
    #else
    
        msg(fmt, bestid, centerit?"*":"",
    
    	bestpos.x[0],bestpos.x[1],bestpos.x[2], wpostr,
    
    	bestsl->text ? bestsl->text : sp->title,
    	vlength( &cpos ), nhits,
    
    
        if(centerit) {
    
    slevy's avatar
    slevy committed
    	view->center( &wpos );
    	if(ppui.censize > 0) view->redraw();
    
        }
      } else {
    
    
    #ifdef FLHACK
        _pview_pickmsg("Picked nothing (%d hits)", nhits);
    #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 ) {
    
      if(parti_parse_args( &st, argc, argv, NULL ))
    
    slevy's avatar
     
    slevy committed
      for(int i = 0; i < MAXSTUFF; i++) {
    
    	const char *onoff = st->useme ? "on" : "off";
    	if(verbose) {
    	    if(st->alias)  msg("g%d=%s: (%s)", i, st->alias, onoff);
    	    else msg("g%d: (%s)", i, onoff);
    	}
    
    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;
    }
    
    
    
    static int stereo_mode = FL_STEREO | FL_RGB | FL_DOUBLE | FL_DEPTH | FL_MULTISAMPLE;
    
    int try_quad_stereo()
    
        return(Fl_Gl_Window::can_do( stereo_mode ));
    
    }
    
    #else  /* backward compatibility -- quadbuffered stereo under Unix/X only */
    
    static int *stereo_mode = NULL;
    
    
    
    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 try_quad_stereo()
    {
        stereo_mode = attrs;
    
    #ifdef GLX_SAMPLE_BUFFERS_SGIS
    
        if(Fl_Gl_Window::can_do( stereo_mode ))
    	return 1;
        stereo_mode += 4;
    
    #endif
    
        if(Fl_Gl_Window::can_do( stereo_mode ))
    	return 1;
        return 0;
    }
    
    int try_quad_stereo() { return 0; }
    #endif
    
    
    char *parti_stereo( const char *ster )
    {
      static char rslt[28];
      static enum Gv_Stereo stereotype = GV_MONO;
    
      static int quadmode = -1;
    
      static char *sternames[] = { "mono", "redcyan", "crosseyed", "glasses", "left", "right" };
    
      int smode = -1;
    
      if(stereotype == GV_MONO) {
        quadmode = try_quad_stereo();
    
        stereotype = (quadmode!=0) ? GV_QUADBUFFERED : GV_REDCYAN;
    
      }
    
      if(ster) {
        float v;
    
    slevy's avatar
    slevy committed
        int pixoff;
        char *cp = strchr(ster, '@');
        if(cp && sscanf(cp+1, "%d", &pixoff)) {
    	ppui.view->stereooffset(pixoff);
    	smode = stereotype;
        }
        char *sp = strpbrk(ster, "-.0123456789");
        if(sp && (cp==NULL || sp<cp) && sscanf(sp, "%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 > 0) {
    
    	    smode = stereotype = GV_QUADBUFFERED;
    	} else {
    	    msg("Can't do quadbuffered/hardware stereo");
    	    smode = GV_MONO;
    	}
        }
    
        else if(strstr(ster, "cross"))
    
    slevy's avatar
    slevy committed
    	smode = stereotype = GV_CROSSEYED;
    
        else if(strstr(ster, "left"))
    	smode = stereotype = GV_LEFTEYE;
        else if(strstr(ster, "right"))
    	smode = stereotype = GV_RIGHTEYE;
    
        if(smode < 0) {
    
    	msg("stereo: [on|off|redcyan|glasses|crosseyed|left|right] [stereosep[@pixeloffset]]");
    
    	msg(" Try stereosep values from 0.02 to 0.1, or -.02 .. -.1 to swap eyes");
        }
      }
    
      if(smode >= 0) {
        if(smode == (int)GV_QUADBUFFERED)
    
        else
    
            ppui.view->glmode( 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]);
    
    slevy's avatar
    slevy committed
      int pixoff = ppui.view->stereooffset();
      if(pixoff)
    	sprintf(rslt+strlen(rslt), "@%d", pixoff);
      
    
      return rslt;
    }
    
    
    int y_root( Fl_Widget *w ) {
      if(w == NULL) return -1;
      int y = w->y();
      Fl_Window *pa;
      for(pa = w->window(); pa != NULL; pa = pa->window())
        y += pa->y();
      return y;
    }
    
    
    Fl_Gview *make_view_window() {
      if(ppui.freewin == NULL) {
        Fl_Group::current( NULL );
    
        Fl_Double_Window *o = new Fl_Double_Window(512, 512);
    
        ppui.freemain = o;
    
        ppui.freewin = new Fl_Gview(0, 0, 512, 512);
        o->add( ppui.freewin );
        Fl_Group::current( NULL );
      }
      return ppui.freewin;
    }
     
    
    void parti_detachview( const char *how ) {
    
      int seeui = 1;
      if(how == NULL) how = "fullscreen";
      Fl_Gview *w = ppui.view;
    
    
    //fprintf(stderr, "detach(%s)\n", how);
    
      switch(how[0]) {
      case 'h': /* full screen, hidden UI */
    	seeui = 0;
      case 'f': /* fullscreen detached window */
    	w = make_view_window();
    	ppui.freemain->fullscreen();
    
    	parti_update();
    
    	ppui.freemain->show();
    
    	parti_update();
    	w->resize( 0, 0, ppui.freemain->w(), ppui.freemain->h() );
    
    	ppui.freemain->init_sizes();
    
    	break;
    
      case 'd': /* ordinary detached window */
    	w = make_view_window();
    
    	if(ppui.view && ppui.view->w() * ppui.view->h() > 0) {
    
    	    ppui.freemain->fullscreen_off(50, 50, ppui.view->w(), ppui.view->h());
    
    	    w->size( ppui.view->w(), ppui.view->h() );
    
    	} else {
    	    ppui.freemain->fullscreen_off(50, 50, 512, 512);
    	}
    	ppui.freemain->show();
    
    	if(w->window() && w->window()->visible()) w->show();
    
    slevy's avatar
    slevy committed
    	msg("detach: huh?  expected \"detach fullscreen\" or \"detach hide\" or just \"detach\"");
    
      if(w != NULL) {
        int mainy0 = y_root( ppui.mainwin );
        int viewy0 = y_root( ppui.cmd ) + ppui.cmd->h();
    
        int mainheight = viewy0 - mainy0;
    
        Fl_Widget *resiz = ppui.maintile->resizable();
    
        if(resiz && resiz != ppui.maintile) {
    	ppui.maintile->resizable(ppui.maintile);
    
    	ppui.maintile->remove( resiz );
        }
    
        ppui.mainwin->size( ppui.mainwin->w(), mainheight );
    
        ppui.mainwin->init_sizes();
        ppui.maintile->init_sizes();
        ppui.maintile->redraw();
    
        ppui.mainwin->redraw();
    
        // parti_update();
    
        if(w != ppui.view) {
    	w->initfrom( ppui.view );
    	ppui.view = w;
    	/* Fix up dangling references to old view */
    	ppui.view->picker( specks_picker, ppui.view );
    	if(ppui.hrdiag) ppui.hrdiag->picker( specks_picker, ppui.view );
        }
    
        ppui.detached = how[0];	// 'h' or 'f' or 'd'
        parti_redraw();
        ppui.boundwin->hide();
        if(seeui) {
    	ppui.mainwin->set_non_modal();
    	ppui.mainwin->show();		// pop control strip to top
        } else {
    	ppui.view->show();
    	ppui.mainwin->hide();
        }
      }
    
    
      /* Is there a main-window-position spec included in our param? */
    
      for(cp = how; *cp; cp++) {
          if(*cp == '-' || *cp == '+') {
    	  char cx[2], cy[2];
    	  int xpos, ypos;
    	  if(4 == sscanf(cp, "%1[-+]%d%1[-+]%d", cx, &xpos, cy, &ypos)) {
    	      Fl_Window *mw = ppui.mainwin;
    	      mw->position( cx[0]=='+' ? xpos : Fl::w() - xpos - mw->w(),
    		      	    cy[0]=='+' ? ypos : Fl::h() - ypos - mw->h() );
    	      break;
    	  }
          }
      }
    
    
    char *parti_winsize( CONST char *newsize ) {
    
      int posx = 0, posy = 0, has_pos = 0;
    
    //fprintf(stderr, "winsize(%s)\n", newsize);
    
      Fl_Window *top = (ppui.freewin != NULL) ?
    		ppui.freemain : ppui.mainwin;
    
      int ox = ppui.view->w(), oy = ppui.view->h();
    
    slevy's avatar
    slevy committed
      if(!top->shown()) {
    
        int nx = ox, ny = oy;
    
        char *posp = strpbrk(newsize, "-+");
        char cx[2], cy[2];
    
        if(posp != newsize && sscanf(newsize, "%d%*c%d", &nx, &ny) == 1)
    
    	ny = nx * oy / ox;
    
        if(posp && 4 == sscanf(posp, "%1[-+]%d%1[-+]%d", cx, &posx, cy, &posy)) {
    	if(cx[0] == '-') posx = Fl::w() - nx - posx;
    
    	if(cy[0] == '-') posy = Fl::h() - ny - posy;
    
        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 || has_pos) {
    
    	if(ppui.freewin != NULL) {
    
    	    parti_detachview( ppui.detached=='h' ? "hide" : "detach" );
    
    	    top->border( 1 );
    
    	    if(has_pos) top->position( posx, posy );
    
    	    top->size( nx, ny );
    
    slevy's avatar
    slevy committed
    	    ppui.view->size( nx, ny );
    
    	    if(has_pos)
    		top->position( posx, posy );
    
    slevy's avatar
    slevy committed
    	    top->size( top->w() + nx - ox,
    		       top->h() + ny - oy );
    
    	    ppui.view->size( nx, ny );
    
    	if(top->visible())
    	    ppui.view->show();
    
    	parti_update();
    	return parti_winsize(NULL);
        }
      }
    
      sprintf(cursize, has_pos ? "%dx%d+%d+%d" : "%dx%d", ox, oy, posx, posy);
    
      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) {
    
        return parti_idof( ppui.st );
    
      }
      if(ppui.view == NULL)
    
        return OBJNO_NONE;
    
      if(!strcmp(objname, "c") || !strcmp(objname, "c0")) {
        ppui.view->target( GV_ID_CAMERA );
    
        return OBJNO_NONE;
    
      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 OBJNO_NONE;
    
        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 OBJNO_NONE;
    
    int parti_idof( struct stuff *st ) {
      for(int i = 0; i < MAXSTUFF; i++)
        if(stuffs[i] == st) return i;
    
      return OBJNO_NONE;
    
    float parti_focal( CONST char *newfocal ) {
    
      float focallen;
      if(newfocal && sscanf(newfocal, "%f", &focallen) > 0)
        ppui.view->focallen( focallen );
      return ppui.view->focallen();
    }
    
    
    int parti_focalpoint( int argc, char **argv, Point *focalpoint, float *minfocallen )
    
    slevy's avatar
    slevy committed
    {
        int ison;
        Point fpt;
        float mlen;
        int changed = 0;
    
        ison = ppui.view->focalpoint( &fpt, &mlen );
    
        if(argc > 0) {
    	if(isalpha(argv[0][0])) {
    	    int nowon = getbool(argv[0], -1);
    	    if(nowon < 0)
    		argc = 0;
    	    else {
    		ison = nowon;
    		changed = 1;
    		argc--;
    		argv++;
    	    }
    	}
    	if(getfloats( &fpt.x[0], 3, 0, argc, argv ) ||
    		getfloats( &mlen, 1, 3, argc, argv ) ||
    		changed)
    	    ppui.view->focalpoint( &fpt, mlen, ison );
        }
        ison = ppui.view->focalpoint( &fpt, &mlen );
        if(focalpoint)
    	*focalpoint = fpt;
        if(minfocallen)
    	*minfocallen = mlen;
        return ison;
    }
    
    
    
    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, char **params )
    
    slevy's avatar
    slevy committed
    {
      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;
    
    slevy's avatar
    slevy committed
    void parti_hrdiag_on( int on ) {
      if(on) {
        ppui.hrdiagwin->show();
        ppui.hrdiag->show();
      } else {
        ppui.hrdiagwin->hide();
      }
    }
    
    
    int parti_inertia( int on ) {
      if(on >= 0)
        ppui.view->inertia( on );
      return ppui.view->inertia();
    }
    
    
    void parti_setpath( int nframes, struct wfframe *frames, float fps = 30, float *focallens = 0 ) {
    
      struct wfpath *path = &ppui.path;
      if(path->frames != NULL)
        free(path->frames);
    
      if(path->focallens != NULL)
        free(path->focallens);
    
      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(focallens != 0) {
    	path->focallens = NewN( float, nframes );
    	memcpy(path->focallens, focallens, nframes*sizeof(float) );
        }
    
      }
    
      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( const char *fname ) {
    
      int nframes, room;