/* * 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> #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 */ 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]; char attrs[1024]; 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, attrs ); #endif if(centerit) { 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); #endif } } void draw_specks( Fl_Gview *view, void *vst, void *junk ) { struct stuff *st = (struct stuff *)vst; 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 ) { struct stuff *st = stuffs[0]; if(parti_parse_args( &st, argc, argv, NULL )) return; for(int i = 0; i < MAXSTUFF; i++) { st = stuffs[i]; if(st) { 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); } specks_parse_args( &st, argc, argv ); } } } void parti_redraw() { if(ppui.view) ppui.view->redraw(); #ifndef FLHACK if(ppui.hrdiag->visible_r()) ppui.hrdiag->redraw(); #endif } 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 void set_wanted_dspcontext( Fl_Gview *view, int detached ) { view->dspcontext( (detached ? 2 : 0) + (view->stereo() == GV_QUADBUFFERED ? 1 : 0) ); } #ifdef GLUT_STEREO 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; #if defined(unix) && !defined(FLHACK) 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; } #else /* win32 or FLHACK */ int try_quad_stereo() { return 0; } #endif #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; int pixoff; const char *cp = strchr(ster, '@'); if(cp && sscanf(cp+1, "%d", &pixoff)) { ppui.view->stereooffset(pixoff); smode = stereotype; } const 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")) 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) ppui.view->glmode( stereo_mode ); else ppui.view->glmode( FL_RGB | FL_DOUBLE | FL_DEPTH | FL_MULTISAMPLE ); ppui.view->stereo( (enum Gv_Stereo)smode ); set_wanted_dspcontext( ppui.view, (ppui.view == ppui.freewin)/*detached*/ ); } smode = ppui.view->stereo(); sprintf(rslt, "%g %s", ppui.view->stereosep(), (smode<=0||smode>=COUNT(sternames)) ? "off" : sternames[smode]); 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(); w->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(); break; default: msg("detach: huh? expected \"detach fullscreen\" or \"detach hide\" or just \"detach\""); break; } 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; set_wanted_dspcontext( ppui.view, 1 ); /* 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? */ const char *cp; 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 ) { static char cursize[24]; #ifdef FLHACK sprintf(cursize, "-1x-1"); #else 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(); if(!top->shown()) { ox = oy = -1; } if(newsize) { int nx = ox, ny = oy; const char *posp = strpbrk(newsize, "-+"); char cx[2], cy[2]; posx = top->x(); posy = top->y(); 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; has_pos = 1; } 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 ); ppui.view->size( nx, ny ); } else { if(has_pos) top->position( posx, posy ); 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); #endif return cursize; } char *parti_clip( const char *nearclip, const char *farclip ) { float n, f; int changed = 0; if(nearclip != NULL && sscanf(nearclip, "%f", &n) > 0) { ppui.view->nearclip( n ); changed = 1; } if(farclip != NULL && sscanf(farclip, "%f", &f) > 0) { ppui.view->farclip( f ); 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; } 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; #if unix 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); #endif } else { 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) = 0; 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 fclose(p); /* win32 */ 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; int gname = 0; char c; 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)) { gname = 1; } 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(i > 0) { if(stuffs[i] == NULL) { stuffs[i] = specks_init( 0, NULL ); stuffs[i]->clk = ppui.clk; ppui.view->add_drawer( draw_specks, stuffs[i], NULL, NULL, i ); ppui.view->picker( specks_picker, ppui.view ); 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 ) { 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 ); } 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] ) { 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) ); } int parti_make_subcam( CONST char *name, int argc, 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 ); ppui.view->Tc2subc( &Tc2subc ); ppui.view->subc_lrbt( subclrbt ); 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] = ""; 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); } struct Fl_Plot *parti_register_plot( struct stuff *st, void (*draw)(struct Fl_Plot *, void *obj, void *arg), void *arg ) { 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_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; int first = 1; struct wfframe *fr, *cf; FILE *f; char line[256], *cp; int lno = 0; int got; float *focallens; int nfocallens = 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 = NewN( struct wfframe, room ); focallens = NewN( float, room ); while(fgets(line, sizeof(line), f) != NULL) { lno++; for(cp = line; isspace(*cp); cp++) ; if(*cp == '#' || *cp == '\0') continue; if(nframes >= room) { room *= 3; fr = RenewN( fr, struct wfframe, room ); focallens = RenewN( focallens, float, room ); } cf = &fr[nframes]; got = sscanf(cp, "%f%f%f%f%f%f%f%f", &cf->tx,&cf->ty,&cf->tz, &cf->rx,&cf->ry,&cf->rz, &cf->fovy, &focallens[nframes]); if(got < 7) { msg( "readpath: %.200s line %d: expected tx ty tz rx ry rz fovy [focallen], got: %.120s", fname, lno, line); fclose(f); return 0; } if(got == 8) nfocallens++; nframes++; } fclose(f); parti_setpath( nframes, fr, 30, nfocallens==nframes ? focallens : 0 ); Free(fr); Free(focallens); parti_setframe( 1 ); msg("%d frames in %.200s", nframes, fname); return 1; } static void fd_got_data( int fd, void * ) { specks_check_async( &ppui.st ); } #ifndef FLHACK void parti_asyncfd(int fd) { Fl::add_fd( fd, fd_got_data ); } void parti_unasyncfd(int fd) { Fl::remove_fd(fd); } #else void parti_asyncfd(int fd) { } void parti_unasyncfd(int fd) { } #endif static void playidle( void * ) { struct wfpath *path = &ppui.path; if(!ppui.playing) { Fl::remove_idle( playidle, NULL ); ppui.playidling = 0; return; } 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; ppui.playtimebase = now; } else { 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, NULL ); ppui.playidling = 0; parti_play( "stop" ); 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; ppui.playtimebase = now; parti_setframe( 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( frame ); needidle = 1; } else { frame += (ppui.playspeed > 0) ? 1 : -1; dt = (frame - ppui.framebase) / (ppui.playspeed * path->fps) + ppui.playtimebase - now; needidle = (dt < .05); } if(needidle && !ppui.playidling) { Fl::add_idle( playidle, NULL ); } if(!needidle && ppui.playidling) { Fl::remove_idle( playidle, NULL ); } ppui.playidling = needidle; if(!needidle) { fprintf(stderr, "<%.3f>", dt); Fl::add_timeout( dt, playidle, NULL ); } } void parti_play( const char *rate ) { struct wfpath *path = &ppui.path; int playnow = 1; int awaitdone = 0; 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(strstr(rate, "wait")) awaitdone = 1; if(ep == rate) { /* No leading number -- did we get just "-" or "-?" or "-help"? */ if(0==strcmp(rate, "-") || strchr(rate, '?') || strchr(rate, 'h')) { msg("Usage: play [rate][s][l|k][f|r][wait] e.g. \"play\" or \"play 30\" or \"play 10kf wait\""); msg(" rate frames/sec; [s]et speed, don't play now; [l]oop/roc[k]"); msg(" play every [f]rame/skip to maintain [r]ate; [wait] until done"); return; } if(sp == 0) playnow = 0; else ppui.playspeed = sp; } } /* Always stop any ongoing timers */ Fl::remove_idle( playidle, NULL ); Fl::remove_timeout( playidle, NULL ); ppui.playing = 0; ppui.playidling = 0; if(playnow) { 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( ppui.framebase ); Fl::add_idle( playidle, NULL ); ppui.playing = 1; ppui.playidling = 1; } #ifndef FLHACK if(ppui.play != NULL) { ppui.play->label( ppui.playing ? "stop" : "play" ); ppui.play->value( ppui.playing ); ppui.play->redraw(); } #endif //FLHACK if(awaitdone) { /* Don't let this command return until the "play" is finished, or at least stopped. * But allow user input along the way. */ while(ppui.playing) Fl::wait(0.1); } } 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( CONST char *frameno, CONST struct wfpath **pp ) { if(pp != NULL) *pp = &ppui.path; if(frameno != NULL) { parti_play( "stop" ); return parti_setframe( atoi(frameno) ); } return (ppui.path.frames == NULL || ppui.path.nframes <= 0) ? -1 : ppui.path.curframe; } int parti_setframe( 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(path->focallens != NULL) ppui.view->focallen( path->focallens[fno - path->frame0] ); #ifndef FLHACK 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 ); } #endif //FLHACK 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(); } void parti_set_speed( struct stuff *st, double speed ) { #ifndef FLHACK ppui.stepspeed->truevalue( speed ); #endif //FLHACK } void parti_set_timebase( struct stuff *st, double timebase ) { char str[32]; ppui.timebasetime = timebase; sprintf(str, "%.16lg", timebase); #ifndef FLHACK ppui.timebase->value( str ); #endif //FLHACK 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); #ifndef FLHACK ppui.timestep->value( str ); if( ppui.timestep->visible_r() && ppui.timebase->visible_r() ) { //steven marx: version 0.7.0 reduce cmdhist flicker ppui.timestep->redraw(); ppui.timebase->redraw(); } #endif //FLHACK } void pp_stepper( void *vst ) { struct stuff *st = (struct stuff *)vst; if(st) { clock_tick( st->clk ); specks_set_timestep( ppui.st ); parti_redraw(); Fl::check(); } } void parti_set_running( struct stuff *st, int on ) { if(clock_running(st->clk) != on) { if(on) Fl::add_idle( pp_stepper, st ); else Fl::remove_idle( pp_stepper, st ); if(st->clk) st->clk->walltimed = 1; clock_set_running(st->clk, on); } #ifndef FLHACK ppui.runstop[0]->value( on && clock_fwd(st->clk) < 0 ); ppui.runstop[1]->value( on && clock_fwd(st->clk) > 0 ); #endif //FLHACK } void parti_set_fwd( struct stuff *st, int fwd ) { int fwdbtn = (fwd > 0); #ifndef FLHACK ppui.runstop[!fwdbtn]->value(0); ppui.runstop[fwdbtn]->value( clock_running( st->clk ) ); #endif //FLHACK }