static char local_id[] = "$Id$"; /* * $Log$ * Revision 1.24 2001/02/19 20:50:33 slevy * Oops, "object" should always invoke parti_object() whether there's an * alias or not! * * Revision 1.23 2001/02/17 22:02:54 slevy * Enlarge polygons so that unit circle is inscribed, not circumscribed. * Then "txscale .5" always shows entire texture regardless of polysides. * Add new "ptsize" command -- makes more sense than "fast". "fast" still works. * * Revision 1.22 2001/02/17 17:44:05 slevy * For polygons, rotate circle of vertices by 1/2 step. * Then, for "polysides 4" and "txscale .707", * the vertices coincide with the corners of the 0..1 texture. * * Revision 1.21 2001/02/17 05:39:45 slevy * Allow (in data language) "object gN=NAME". * * Revision 1.20 2001/02/15 05:41:12 slevy * new textcmap, textcment commands. Regularize color-gamma-mapping. * Object aliases: "object gN=ALIAS", or in command mode, "gN=ALIAS". * Accept "ellipsoid" data tag; not yet implemented. * * Revision 1.19 2001/02/05 00:41:52 slevy * Accept "time" as synonym for "step". * Add "pickrange". * Mention jump, center in help msg. * * Revision 1.18 2001/02/03 23:43:10 slevy * Don't let "kira tree" hide "kira track" -- demand 3 chars! * * Revision 1.17 2001/02/03 16:49:42 slevy * Update cookedcmap when "cment" changes cmap. * * Revision 1.16 2001/02/03 15:29:03 slevy * Toss unused variable. * * Revision 1.15 2001/02/03 14:50:44 slevy * Add "setgamma" (abbr. "setgam" or "cgam") command to adjust colors. * Add "kira tree {off|on|cross|tick} [tickscale]" subcommand * for showing tree structure of interacting groups. * * Revision 1.14 2001/01/31 17:11:54 slevy * Ensure that, for starlab, clock is always in "continuous" mode. * * Revision 1.13 2001/01/31 17:07:12 slevy * Add RCS Id and Log strings. * */ #define __USE_MISC /* makes <math.h> define sqrtf() on GNU libc */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <ctype.h> #if !sgi && !defined(sqrtf) # define sqrtf(x) sqrt(x) /* if no sqrtf() */ #endif #undef isspace /* hack for irix 6.5 back-compat */ #undef isdigit #undef isalnum #if unix # include <unistd.h> # include <sys/types.h> # include <netinet/in.h> /* for htonl */ # include <time.h> # include <alloca.h> # ifndef WORDS_BIGENDIAN # include "config.h" /* for WORDS_BIGENDIAN */ # endif #else /*WIN32*/ # include "winjunk.h" # define WORDS_BIGENDIAN 0 #endif #include <string.h> #include <errno.h> #if WORDS_BIGENDIAN #define RGBALPHA(rgb, alpha) ((rgb) | (alpha)) #define RGBWHITE 0xFFFFFF00 #define PACKRGBA(r,g,b,a) ((r)<<24 | (g)<<16 | (b)<<8 | (a)) #define THRESHBIT PACKRGBA(0,0,0,1) #else #define RGBALPHA(rgb, alpha) ((rgb) | ((alpha)<<24)) #define RGBWHITE 0x00FFFFFF #define PACKRGBA(r,g,b,a) ((a)<<24 | (b)<<16 | (g)<<8 | (r)) #define THRESHBIT PACKRGBA(0,0,0,1) #endif #if CAVE # define CAVEMENU 1 #endif #include "geometry.h" #include <GL/gl.h> #if CAVEMENU #include <cave_ogl.h> #include "cavemenu.h" #include "vd_util.h" #define IFMENU(x) (x) #else #define IFMENU(x) #endif #include "shmem.h" /* NewN(), etc. */ #include "futil.h" #include "specks.h" #include "textures.h" #include "findfile.h" #include "partiviewc.h" #include "sfont.h" #include <sys/types.h> #include <signal.h> #ifdef sgi #include <malloc.h> /* for mallinfo(), amallinfo() */ #endif #ifdef USE_KIRA #include "kira_parti.h" #endif /* Star Renderer (.sdb) structure -- from stardef.h */ typedef enum {ST_POINT, ST_BRIGHT_CLOUD ,ST_DARK_CLOUD, ST_BOTH_CLOUD, OFF} stype; typedef struct { float x, y, z; float dx, dy, dz; float magnitude, radius; float opacity; int num; unsigned short color; unsigned char group; unsigned char type; } db_star; typedef struct hrec { float t; int num;} hrec_t; typedef struct mrec { float mass, x, y, z, vx, vy, vz, rho, temp, sfr, gasmass; int id, token;} mrec_t; /* end Star Renderer */ #define VDOT( v1, v2 ) ( (v1)->x[0]*(v2)->x[0] + (v1)->x[1]*(v2)->x[1] + (v1)->x[2]*(v2)->x[2] ) #if CAVEMENU int parti_menuwall, parti_menubox[4]; static MenuHead *pmenu = NULL; static MenuHead *stubmenu = NULL; struct ments { MenuEnt *alpha, *point, *poly, *label, *psize, *polysize, *fog; MenuEnt *step, *fwd, *lumvar, *slum, *colorvar, *where[4]; MenuEnt *viewall, *setscale, *speed, *fspeed, *every; MenuEnt *seedata, *box, *boxlevel[12]; MenuEnt *annot; MenuEnt *tknob[3]; MenuEnt *menu; } ment; struct boxleveler { struct stuff *st; int level; }; void set_tknob( float val, MenuEnt *me, void *st ); void set_psize( float psize, MenuEnt *me, void *st ); void set_polysize( float psize, MenuEnt *me, void *st ); void set_alpha( float alpha, MenuEnt *me, void *st ); void set_point( int on, MenuEnt *me, void *st ); void set_poly( int on, MenuEnt *me, void *st ); void set_label( int on, MenuEnt *me, void *st ); void set_step( float time0, MenuEnt *me, void *st ); void set_fwd( int fwd, MenuEnt *me, void *st ); void set_slum( float slum, MenuEnt *me, void *st ); void set_lumvar( int which, MenuEnt *me, void *st ); void set_colorvar( int which, MenuEnt *me, void *st ); void set_seedata( int which, MenuEnt *me, void *st ); void set_speed( float speed, MenuEnt *me, void *st ); void set_fspeed( float fspeed, MenuEnt *me, void *st ); void set_every( float every, MenuEnt *me, void *st ); void set_scale( float logscale, MenuEnt *me, void *st ); void set_viewall( int all, MenuEnt *me, void *st ); void set_allboxes( int on, MenuEnt *me, void *st ); void set_boxlevel( int on, MenuEnt *me, void *bleveler ); void set_where( int unused, MenuEnt *me, void *st ); void set_menu( int on, MenuEnt *me, void *st ); #endif /*CAVEMENU*/ static int defcmap[] = { 0x11eeee00, 0x1106ee00, 0x120ea900, 0x1316ce00, 0x1520d500, 0x172ece00, 0x193fcb00, 0x1c54b400, 0x206da400, 0x24889200, 0x2aa58400, 0x33c07600, 0x3ed96a00, 0x4eed6100, 0x63fa5a00, 0x7efe5100, 0x99fb4c00, 0xafef4700, 0xc0dc4000, 0xcbc33900, 0xd4a83000, 0xda8b2900, 0xdf702700, 0xe2572500, 0xe5411700, 0xe82f1000, 0xea211500, 0xeb171400, 0xed0f1200, 0xee0aee00, 0xffffff00, }; int orientboxcolor = PACKRGBA( 0xff, 0xff, 0, 0xff ); void specks_read( struct stuff **stp, char *fname ); void specks_read_boxes( struct stuff *st, char *fname, int timestep ); int specks_add_box( struct stuff *st, struct AMRbox *box, int timestep ); int specks_purge( void *vstuff, int nbytes, void *aarena ); int specks_count( struct specklist *head ); int specks_gobox( struct stuff *st, int boxno, int argcrest, char *argvrest[] ); int specks_cookcment( struct stuff *st, int cment ); void specks_rgbremap( struct stuff *st ); void specks_reupdate( struct stuff *st, struct specklist *sl ); struct specklist **specks_find_annotation( struct stuff *, struct specklist **); void specks_set_current_annotation( struct stuff *st, char *annotation ); void specks_add_annotation( struct stuff *st, char *annotation, int timestep ); void specks_timerange( struct stuff *st, double *tminp, double *tmaxp ); extern int getbool( char *str, int defval ); extern double getfloat( char *str, double defval ); static char separ[] = " \t\n"; void strncpyt( char *dst, char *src, int dstsize ) { int len = strlen(src); if(len >= dstsize) len = dstsize-1; memcpy(dst, src, len); dst[len] = '\0'; } #ifdef sgi static float defgamma = 1.0; #else static float defgamma = 2.5; #endif struct stuff * specks_init( int argc, char *argv[] ) { int i; struct stuff *st = NewN( struct stuff, 1 ); char *menuopt; memset(st, 0, sizeof(*st)); st->spacescale = 1.0; st->fog = 0; st->psize = 1; st->alpha = .5; st->gamma = defgamma; st->rgbgamma[0] = st->rgbgamma[1] = st->rgbgamma[2] = 1.0; st->rgbright[0] = st->rgbright[1] = st->rgbright[2] = 1.0; specks_rgbremap( st ); st->alias = NULL; st->useme = 1; st->usepoly = 0; st->usepoint = 1; st->usetext = 1; st->usetextaxes = 1; st->usetextures = 1; st->useboxes = 1; st->polysizevar = -1; st->polyarea = 0; st->polyorivar0 = -1; st->texturevar = -1; st->txscale = .5; st->boxlabels = 0; st->boxlabelscale = 1.0; st->boxlevelmask = ~0; /* all levels on */ st->boxaxes = 0; /* boxes don't show orientation markers */ st->goboxscale = 1.0; st->textsize = .05; st->npolygon = 11; st->subsample = 1; st->everycomp = 1; st->menudemandfps = 4.0; st->pfaint = .05; /* params for "fast" point-drawing */ st->plarge = 10; st->polymin = .5; /* don't draw polygons if smaller (pixels) */ st->polymax = 2048; /* don't allow polygons to get bigger than this (pixels) */ st->textmin = 2; /* replace labels with line-segments if smaller (pixels) */ st->ntextures = 0; st->textures = NULL; st->fade = F_SPHERICAL; st->fadeknee1 = 10.0; st->fadeknee2 = 1.0; st->knee2steep = 1.0; st->gscale = 1.; st->gtrans.x[0] = st->gtrans.x[1] = st->gtrans.x[2] = 0; st->objTo2w = Tidentity; st->ncmap = st->boxncmap = st->textncmap = COUNT(defcmap); st->cmap = NewN(struct cment, COUNT(defcmap)); st->boxcmap = NewN(struct cment, COUNT(defcmap)); st->textcmap = NewN(struct cment, COUNT(defcmap)); for(i = 0; i < COUNT(defcmap); i++) { st->cmap[i].raw = st->boxcmap[i].raw = st->textcmap[i].raw = htonl(defcmap[i]); st->cmap[i].cooked = st->boxcmap[i].cooked = st->textcmap[i].cooked = specks_cookcment( st, st->cmap[i].raw ); } /* Ensure that textcmap[0] is white by default */ st->textcmap[0].raw = PACKRGBA( 170, 170, 170, 0 ); st->textcmap[0].cooked = specks_cookcment( st, st->textcmap[0].raw ); st->sizedby = 0; st->coloredby = 1; st->sizeseq = st->colorseq = st->threshseq = 0; st->trueradius = 0; st->sdbvars = shmstrdup( "mcr" ); st->nghosts = 0; st->ntimes = 0; st->ndata = 0; st->curtime = 0; st->curdata = 0; st->datatime = 0; st->usertrange = 0; st->utmin = -HUGE; st->utmax = HUGE; st->utwrap = 0.1; st->sl = NULL; st->boxes = NULL; st->boxlevels = 0; st->boxlinewidth = 0.75; st->depthsort = 0; st->clk = NewN(SClock, 1); clock_init(st->clk); clock_set_running(st->clk, 1); memset(st->anima, 0, sizeof(st->anima)); memset(st->annot, 0, sizeof(st->annot)); memset(st->datafile, 0, sizeof(st->datafile)); memset(st->fname, 0, sizeof(st->fname)); #if CAVE shmrecycler( specks_purge, st ); #endif for(i = 1; i < argc; i++) specks_read( &st, argv[i] ); #if CAVEMENU menu_preinit(); pmenu = menu_create( .05, .95 ); menu_setfont( pmenu, font_small ); ment.annot = menu_addentry( pmenu, " ", NULL, NULL ); menu_addentry( pmenu, " ", NULL, NULL ); menuopt = getenv("PARTIMENU"); if(menuopt == NULL) menuopt = "label"; if(strstr(menuopt, "survey")) { /* SC99DEMO star-formation survey */ for(i = 0; i < 3; i++) { struct boxleveler *tb = NewN( struct boxleveler, 1 ); tb->st = st; tb->level = i; ment.tknob[i] = menu_addknob( pmenu, NULL, set_tknob, 0, 0,4, tb ); } } menu_addentry( pmenu, " ", NULL, NULL ); ment.psize = menu_addknob( pmenu, NULL, set_psize, st->psize, 0.1,20, st ); ment.slum = menu_addknob( pmenu, NULL, set_slum, st->vdesc[st->curdata][st->sizedby].lum, .1, 10, st ); ment.lumvar = menu_addtoggle( pmenu, NULL, set_lumvar, st->sizedby, MAXVAL, st ); ment.colorvar = menu_addtoggle( pmenu, NULL, set_colorvar, st->coloredby, MAXVAL, st ); ment.seedata = menu_addtoggle( pmenu, NULL, set_seedata, st->curdata, 2, st ); if(strstr(menuopt, "label")) ment.label = menu_addtoggle( pmenu, NULL, set_label, st->usetext, 2, st ); if(strstr(menuopt, "poly")) ment.poly = menu_addtoggle( pmenu, NULL, set_poly, st->usepoly, 2, st ); menu_addentry( pmenu, " ", NULL, NULL ); /* spacer */ #ifndef STATIC_SPECKS ment.fspeed = menu_addknob( pmenu, NULL, set_fspeed, clock_speed(st->clk), 0, 5, st ); ment.speed = menu_addknob( pmenu, NULL, set_speed, clock_speed(st->clk), .5, 5, st ); ment.step = menu_addknob( pmenu, NULL, set_step, clock_time(st->clk), 0, 10, st ); ment.fwd = menu_addtoggle( pmenu, NULL, set_fwd, clock_fwd(st->clk), 2, st ); #endif #ifndef NO_WHERE ment.where[0] = menu_addentry( pmenu, "", NULL, NULL ); ment.where[1] = menu_addentry( pmenu, "", NULL, NULL ); ment.where[2] = menu_addentry( pmenu, "", NULL, NULL ); ment.where[3] = menu_addentry( pmenu, "", NULL, NULL ); #endif ment.every = menu_addknob( pmenu, NULL, set_every, st->subsample, 1, 10, st ); menu_addentry( pmenu, " ", NULL, NULL ); ment.setscale = menu_addknob( pmenu, NULL, set_scale, 0., -4., 1., st ); if(strstr(menuopt, "viewall")) ment.viewall = menu_addtoggle( pmenu, NULL, set_viewall, 0, 2, st ); menu_addentry( pmenu, " ", NULL, NULL ); ment.box = menu_addtoggle( pmenu, NULL, set_allboxes, st->useboxes, 3, st ); menu_addentry( pmenu, " ", NULL, NULL ); { struct boxleveler *blev = NewN( struct boxleveler, COUNT(ment.boxlevel) ); for(i = 0; i < COUNT(ment.boxlevel); i++) { blev[i].st = st; blev[i].level = i; ment.boxlevel[i] = menu_addtoggle( pmenu, NULL, set_boxlevel, 1, 2, &blev[i] ); } } ment.menu = menu_addtoggle( pmenu, "<Menu>", set_menu, 0, 2, st ); menu_addentry( pmenu, " ", NULL, NULL ); stubmenu = menu_create( .05, .15 ); menu_addtoggle( stubmenu, "<Menu>", set_menu, 0, 2, st ); menu_sethidden( stubmenu, 1 ); #endif /*NOCAVE*/ return st; } #if CAVEMENU void specks_refresh_menu(struct stuff *st) { int i; int empty = st->ntimes == 0 && st->sl == NULL; #if MENU_IN_PPR menu_check( pmenu, pmenu->cavewall ); menu_check( stubmenu, stubmenu->cavewall ); #endif menu_sethidden( pmenu, st->hidemenu || empty ); menu_sethidden( stubmenu, st->hidemenu<=0 || empty ); if(st->vdcmd[0] != '\0') { VIDI_queue_commandstr( st->vdcmd ); st->vdcmd[0] = '\0'; } set_psize( st->psize, ment.psize, st ); set_lumvar( st->sizedby, ment.lumvar, st ); set_colorvar( st->coloredby, ment.colorvar, st ); set_slum( st->vdesc[st->curdata][st->sizedby].lum, ment.slum, st ); set_every( st->subsample, ment.every, st ); set_scale( ment.setscale->val, ment.setscale, st ); if(ment.label) set_label( st->usetext, ment.label, st ); if(ment.poly) set_poly( st->usepoly, ment.poly, st ); if(ment.annot) menu_settitle( ment.annot, st->annotation ? st->annotation : " " ); if(st->ntimes > 1) { menu_setknobrange( ment.step, 0, st->ntimes-1 ); set_fwd( st->timefwd, ment.fwd, st ); set_step( st->time0, ment.step, st ); set_speed( clock_speed(st->clk), ment.speed, st ); } else { menu_settitle( ment.fwd, "" ); menu_settitle( ment.step, "" ); menu_settitle( ment.speed, "" ); if(ment.viewall) menu_settitle( ment.viewall, "" ); /* no need to "view peak", right? */ } if(st->ndata > 1) { set_seedata( st->curdata, ment.seedata, st ); } else { menu_settitle( ment.seedata, "" ); } if(st->boxlevels > 0) { set_allboxes( st->useboxes, ment.box, st ); } else { menu_settitle( ment.box, "" ); } if(ment.tknob[0]) /* if SC99DEMO, i.e. if PARTIMENU includes "survey" */ for(i = 0; i < 3; i++) set_tknob( ment.tknob[i]->val, ment.tknob[i], ment.tknob[i]->data ); #ifndef NO_WHERE for(i = 0; i < COUNT(ment.where) && ment.where[i] != NULL; i++) set_where( i, ment.where[i], st ); #endif /* Publicize our menu location so other apps -- Matt's vtk AMR code -- * can avoid interacting if user's wand points there. */ parti_menuwall = pmenu->cavewall; parti_menubox[0] = pmenu->x0 * pmenu->wallxpix; parti_menubox[1] = ((pmenu->y0 < pmenu->y1) ? pmenu->y0 : pmenu->y1) * pmenu->wallypix; parti_menubox[2] = pmenu->x1 * pmenu->wallxpix; parti_menubox[3] = ((pmenu->y0 > pmenu->y1) ? pmenu->y0 : pmenu->y1) * pmenu->wallypix; } void specks_evoke_menu( struct stuff *st ) { menu_evoke( pmenu ); /* evoke any callbacks */ } #endif /*CAVEMENU*/ void specks_rethresh( struct stuff *st, struct specklist *sl, int by ) { struct valdesc *vd; int i; int curdata = st->curdata; int nel = sl->nspecks; float cmin, cmax, normal; int ncmap = st->ncmap; int index; int min, max; float threshmin = st->thresh[0], threshmax = st->thresh[1]; struct speck *p = sl->specks; sl->threshseq = st->threshseq; if(sl->text != NULL) /* specklists with labels shouldn't be thresholded */ return; if(curdata >= st->ndata) curdata = 0; min = (SMALLSPECKSIZE(by)>=sl->bytesperspeck) ? 0 : st->usethresh&P_THRESHMIN; max = (SMALLSPECKSIZE(by)>=sl->bytesperspeck) ? 0 : st->usethresh&P_THRESHMAX; for(i = 0, p = sl->specks; i < sl->nspecks; i++, p = NextSpeck( p, sl, 1 )) { p->rgba = (min&&p->val[by]<threshmin) || (max&&p->val[by]>threshmax) ? p->rgba | THRESHBIT : p->rgba & ~THRESHBIT; } } int specks_cookcment( struct stuff *st, int cment ) { unsigned char crgba[4]; memcpy(crgba, &cment, 4); /* XXX 64-bit bug? */ return (cment & ~RGBWHITE) | PACKRGBA( st->rgbmap[0][crgba[0]], st->rgbmap[1][crgba[1]], st->rgbmap[2][crgba[2]], 0 ); } void specks_rgbremap( struct stuff *st ) { int i, k; for(i = 0; i < 256; i++) { float t = (float)i / 256; float v = 255.99f * pow(t, 1/st->rgbgamma[0]); k = (int) (st->rgbright[0] * v); st->rgbmap[0][i] = (k <= 0) ? 0 : (k > 255) ? 255 : k; if(st->rgbgamma[0] != st->rgbgamma[1]) v = 255.99f * pow(t, 1/st->rgbgamma[1]); k = (int) (st->rgbright[1] * v); st->rgbmap[1][i] = (k <= 0) ? 0 : (k > 255) ? 255 : k; if(st->rgbgamma[1] != st->rgbgamma[2]) v = 255.99f * pow(t, 1/st->rgbgamma[2]); k = (int) (st->rgbright[2] * v); st->rgbmap[2][i] = (k <= 0) ? 0 : (k > 255) ? 255 : k; } /* remake colormap too */ for(i = 0; i < st->ncmap; i++) st->cmap[i].cooked = specks_cookcment( st, st->cmap[i].raw ); st->colorseq++; } void specks_recolor( struct stuff *st, struct specklist *sl, int by ) { struct valdesc *vd; int i; int curdata = st->curdata; int nel = sl->nspecks; struct speck *sp = sl->specks; float cmin, cmax, normal; int ncmap = st->ncmap; int index; int rgb565; unsigned char (*rgbmap)[256]; sl->coloredby = by; sl->colorseq = st->colorseq; if(sl->text != NULL) /* specklists with labels shouldn't be recolored */ return; if(curdata >= st->ndata) curdata = 0; if(by == CONSTVAL) { /* Hack -- color by given RGB value */ char crgba[4]; int r, g, b, rgba; vd = &st->vdesc[curdata][CONSTVAL]; r = (vd->cmin<=0) ? 0 : vd->cmin>=1 ? 255 : (int)(255.99f * vd->cmin); g = (vd->cmax<=0) ? 0 : vd->cmax>=1 ? 255 : (int)(255.99f * vd->cmax); b = (vd->mean<=0) ? 0 : vd->mean>=1 ? 255 : (int)(255.99f * vd->mean); crgba[0] = st->rgbmap[0][r]; crgba[1] = st->rgbmap[0][g]; crgba[2] = st->rgbmap[0][b]; crgba[3] = 0; rgba = *(int *)&crgba[0]; /* XXX 64-bit bug? */ for(i = 0; i < nel; i++, sp = NextSpeck(sp, sl, 1)) { sp->rgba = rgba | (sp->rgba & THRESHBIT); } return; } if(by >= MAXVAL || by < 0 || SMALLSPECKSIZE(by) > sl->bytesperspeck) by = 0; vd = &st->vdesc[curdata][by]; cmin = vd->cmin, cmax = vd->cmax; if(cmin == cmax && cmin == 0 || (vd->call && !vd->cexact)) { vd->cmin = cmin = vd->min; vd->cmax = cmax = vd->max; } normal = (cmax != cmin && ncmap>2) ? (ncmap-2)/(cmax - cmin) : 0; rgb565 = !strcmp(vd->name, "rgb565") || !strcmp(vd->name, "colors565"); /* cexact field means: * 0 (default): scale data range to cmap index 1..ncmap-2; * use 0 and ncmap-1 for low- and high- out-of-range values. * * 1 ("exact"): use data value as literal colormap index, 0..ncmap-1. */ rgbmap = &st->rgbmap[0]; for(i = 0; i < nel; i++, sp = NextSpeck(sp, sl, 1)) { if(rgb565) { index = sp->val[by]; sp->rgba = PACKRGBA( rgbmap[0][(index&0xF800)>>(11-(8-5))], rgbmap[1][(index&0x07E0)>>(5-(8-6))], rgbmap[2][(index&0x1F)<<(8-5)], 0 ) | (sp->rgba & THRESHBIT); } else { index = vd->cexact ? sp->val[by] + cmin : (sp->val[by] - cmin) * normal + 1; if(index < 0) index = 0; else if(index >= ncmap) index = ncmap-1; sp->rgba = st->cmap[index].cooked | (sp->rgba & THRESHBIT); } } } void specks_resize( struct stuff *st, struct specklist *sl, int by ) { int i; int nel = sl->nspecks; struct speck *sp = sl->specks; int curdata = st->curdata; struct valdesc *vd; float lmin, lmax, normal; sl->sizedby = by; sl->sizeseq = st->sizeseq; if(sl->text != NULL) /* specklists with labels shouldn't be resized */ return; if(curdata < 0 || curdata >= st->ndata) curdata = 0; if(by == CONSTVAL) { vd = &st->vdesc[curdata][CONSTVAL]; for(i = 0; i < nel; i++, sp = NextSpeck(sp, sl, 1)) sp->size = vd->lmin; return; } if(by < 0 || by >= MAXVAL) by = 0; vd = &st->vdesc[curdata][by]; lmin = vd->lmin, lmax = vd->lmax; if(lmin == lmax && lmin == 0 || vd->lall) { vd->lmin = lmin = vd->min; vd->lmax = lmax = vd->max; } if(lmax == lmin) normal = 1; else normal = 1 / (lmax - lmin); for(i = 0; i < nel; i++, sp = NextSpeck(sp, sl, 1)) sp->size = (sp->val[by] - lmin) * normal; } void specks_datawait(struct stuff *st) { #if USE_IEEEIO while(st->fetching && st->fetchpid > 0 && st->fetchtime == st->curtime && st->fetchdata == st->curdata) usleep(50000); #endif } #ifdef USE_IEEEIO void specks_ieee_server( void *vst ) { struct stuff *st = (struct stuff *)vst; struct specklist *sl; prctl(PR_TERMCHILD); /* Die when parent dies */ /* Await a request */ for(;;) { time_t then; while(st->fetching <= 0) sginap(5); then = time(NULL); sl = specks_ieee_read_timestep( st, st->subsample, st->fetchdata, st->fetchtime ); if(then + 5 < time(NULL)) msg("... got %x (%d particles) from %s", sl, specks_count(sl), st->fname[st->fetchdata][st->fetchtime]); if(st->curtime == st->fetchtime && st->curdata == st->fetchdata && sl != NULL) { st->sl = sl; parti_redraw(); } st->fetching = 0; } } #endif /*USE_IEEEIO*/ void specks_set_speed( struct stuff *st, double newspeed ) { clock_set_speed( st->clk, newspeed ); parti_set_speed( st, newspeed ); } void specks_set_fspeed( struct stuff *st, double newspeed ) { /* ignore it! */ /* or maybe use fspeed to specify finite clock resolution */ } void specks_shift_ghosts( struct stuff *st ) { int i; struct specklist **slp = specks_timespecksptr( st, 0, st->nghosts > 0 ? st->nghosts-1 : 0 ); if(slp && *slp) specks_discard( st, slp ); for(i = st->nghosts; --i > 0; ) { slp = specks_timespecksptr( st, 0, st->nghosts ); *slp = slp[-1]; } *specks_timespecksptr( st, 0, 0 ) = NULL; } void specks_set_timestep( struct stuff *st ) { struct specklist *sl = st->sl; struct specklist **slp; double realtime, tmin, tmax; int timestep; st->used++; specks_timerange( st, &tmin, &tmax ); if(st->usertrange) { if(tmin < st->utmin) tmin = st->utmin; if(tmax > st->utmax) tmax = st->utmax; } clock_set_range( st->clk, tmin, tmax, st->utwrap ); realtime = clock_time( st->clk ); timestep = realtime; if(st->dyndata && st->dyndatafunc) { struct specklist *sl; if(realtime == st->currealtime && st->sl != NULL && st->sl->used >= 0) return; sl = (*st->dyndatafunc)(st, realtime); sl->used = 1; st->currealtime = realtime; st->curtime = timestep; /* Possibly retain old snapshots -- up to st->ntimes of them! */ /* In any case, discard previous array */ specks_shift_ghosts( st ); slp = specks_timespecksptr( st, 0, 0 ); *slp = st->sl = sl; parti_set_timestep( st, realtime ); /* Hack for kira-parti dynamic data */ if(sl) { if(st->coloredby >= 0 && st->coloredby < CONSTVAL) st->colorseq++; if(st->sizedby >= 0 && st->coloredby < CONSTVAL) st->sizeseq++; if(st->threshvar >= 0) st->threshseq++; } return; } if(timestep >= st->ntimes) timestep = st->ntimes - 1; if(timestep < 0) timestep = 0; slp = specks_timespecksptr( st, st->curdata, timestep ); if(*slp != NULL && (*slp)->subsampled > st->subsample) { specks_discard( st, slp ); } sl = *slp; #ifdef USE_IEEEIO if(sl == NULL && (st->fetching == 0 || st->datasync) && st->curdata>=0 && st->curdata<st->ndata && st->datafile[st->curdata] != NULL && timestep>=0 && timestep<st->ntimes && st->datafile[st->curdata][timestep] != NULL) { st->fetchdata = st->curdata; st->fetchtime = timestep; if(getenv("NO_SPROC") || st->datasync) { sl = specks_ieee_read_timestep( st, st->subsample, st->fetchdata, st->fetchtime ); msg("Got %x (%d particles) from %s (d%d t%d)\n", sl, specks_count(sl), st->fname[st->fetchdata][st->fetchtime], st->fetchdata, st->fetchtime); } else { st->fetching = 1; if(st->fetchpid <= 0) { st->fetchpid = sproc( specks_ieee_server, PR_SADDR|PR_SFDS, st ); if(st->fetchpid < 0) perror("sproc"); } } } if(st->fetchpid > 0) { if(kill(st->fetchpid, 0) < 0) { perror("specks server vanished: kill -0"); st->fetching = 0; st->fetchpid = 0; } } #endif /*USE_IEEEIO*/ if(sl == NULL && !(st->boxtimes>timestep && st->boxes[timestep]!=NULL)) return; st->sl = sl; /* st->sl <= anima[][] */ st->curtime = timestep; st->currealtime = timestep; #if !CAVEMENU st->frame_time = st->curtime; /* if non-CAVE, we have no frame-function */ #endif specks_set_current_annotation( st, st->annot[st->curdata][st->curtime] ? st->annot[st->curdata][st->curtime]->text : NULL ); parti_set_timestep( st, timestep ); } void specks_set_current_annotation( struct stuff *st, char *annotation ) { st->annotation = annotation; #ifdef CAVEMENU if(ment.annot) menu_settitle( ment.annot, st->annotation ? st->annotation : "" ); #endif } void specks_add_annotation( struct stuff *st, char *annotation, int timestep ) { struct specklist *sl, **slp; int curtime = (timestep < 0) ? st->curtime : timestep; if(annotation == NULL) annotation = ""; specks_timespecksptr( st, st->curdata, curtime ); if(curtime >= st->ntimes) curtime = 0; slp = &st->annot[st->curdata][curtime]; if((sl = *slp) == NULL) { sl = *slp = NewN( struct specklist, 1 ); memset(*slp, 0, sizeof(**slp)); } else if(sl->text) { Free(sl->text); } sl->text = NewN( char, strlen(annotation)+1 ); strcpy( sl->text, annotation ); } #if !WORDS_BIGENDIAN void starswap(db_star *st) { int i, *wp; /* byte-swap x,y,z, dx,dy,dz, magnitude,radius,opacity fields (32-bit float), * num (32-bit int), * color (16-bit short). * group and type fields shouldn't need swapping, * assuming the compiler packs bytes into a word in increasing * address order. Seems safe. */ for(i = 0, wp = (int *)st; i < 10; i++) wp[i] = htonl(wp[i]); st->color = htons(st->color); } #endif /*!WORDS_BIGENDIAN*/ void specks_read_sdb( struct stuff *st, char *sdbfname, int timestep ) { FILE *inf = fopen(sdbfname, "r"); long flen; int nspecks, i; float min[MAXVAL], max[MAXVAL], sum[MAXVAL]; struct specklist *sl, **slp; register struct speck *sp; int dfltvars = (strcmp(st->sdbvars, "mcr") == 0); int nvars = strlen(st->sdbvars); int needswap = (htonl(1) != 1); if(inf == NULL) { msg("sdb: %s: cannot open: %s", sdbfname, strerror(errno)); return; } /* Just measure file size */ errno = 0; fseek(inf, 0, SEEK_END); flen = ftell(inf); if(flen == -1 || flen == 0) { msg("sdb: %s: can't measure length of file: %s", sdbfname, strerror(errno)); return; } nspecks = (flen / sizeof(db_star)); if(nspecks <= 0) { msg("sdb: %s: ignoring empty sdb file", sdbfname); return; } sl = NewN(struct specklist, 1); memset(sl, 0, sizeof(*sl)); sl->bytesperspeck = SMALLSPECKSIZE( nvars ); if(nvars > MAXVAL) nvars = MAXVAL; sl->scaledby = st->spacescale; sp = NewNSpeck(sl, nspecks); sl->specks = sp; fseek(inf, 0, SEEK_SET); for(i = 0; i < nspecks; i++, sp = NextSpeck(sp, sl, 1)) { db_star star; char *cp; int k; float *vp; if(fread(&star, sizeof(star), 1, inf) <= 0) break; #if !WORDS_BIGENDIAN starswap(&star); #endif sp->p.x[0] = star.x * sl->scaledby; sp->p.x[1] = star.y * sl->scaledby; sp->p.x[2] = star.z * sl->scaledby; if(dfltvars) { sp->val[0] = exp((-18-star.magnitude)*.921/*log(100)/5*/); sp->val[1] = star.color; sp->val[2] = star.radius; } else { for(vp = &sp->val[0], cp = st->sdbvars; *cp; cp++, vp++) { switch(*cp) { case 'm': *vp = exp((-18-star.magnitude)*.921/*log(100)/5*/); break; case 'M': *vp = star.magnitude; break; case 'c': *vp = star.color; break; case 'r': *vp = star.radius; break; case 'o': *vp = star.opacity; break; case 'g': *vp = star.group; break; case 't': *vp = star.type; break; case 'x': *vp = star.dx; break; case 'y': *vp = star.dy; break; case 'z': *vp = star.dz; break; case 'S': *vp = sqrt(star.dx*star.dx + star.dy*star.dy + star.dz*star.dz); break; case 'n': *vp = star.num; break; default: *vp = 1; break; } } } if(i == 0) { for(k = 0; k < nvars; k++) sum[k] = min[k] = max[k] = sp->val[k]; } else { for(k = 0; k < nvars; k++) { if(min[k] > sp->val[k]) min[k] = sp->val[k]; else if(max[k] < sp->val[k]) max[k] = sp->val[k]; sum[k] += sp->val[k]; } } } sl->nspecks = i; sl->sizedby = 0; sl->coloredby = 1; /* Update statistics */ if(sl->nspecks > 0) { struct valdesc *vdp = &st->vdesc[st->curdata][0]; for(i = 0; i < nvars; i++, vdp++) { if(vdp->min > min[i]) vdp->min = min[i]; if(vdp->max < max[i]) vdp->max = max[i]; vdp->nsamples += sl->nspecks; vdp->sum += sum[i]; vdp->mean = vdp->sum / vdp->nsamples; if(vdp->name[0] == '\0') { char *name = "unk"; switch(st->sdbvars[i]) { case 'm': name = "lumsdb"; break; case 'M': name = "magsdb"; break; case 'c': name = vdp->max > 16384 ? "rgb565" : "colorsdb"; vdp->cexact = 1; break; case 'r': name = "radius"; break; case 'o': name = "opacity"; break; case 'g': name = "group"; break; case 't': name = "type"; break; case 'x': name = "dx"; break; case 'y': name = "dy"; break; case 'z': name = "dz"; break; case 'S': name = "speed"; break; case 'n': name = "number"; break; } strcpy(vdp->name, name); } } specks_recolor( st, sl, st->coloredby ); specks_resize( st, sl, st->sizedby ); } /* Add to running list */ slp = specks_timespecksptr( st, st->curdata, timestep ); sl->next = *slp; *slp = sl; fclose(inf); } void specks_timerange( struct stuff *st, double *tminp, double *tmaxp ) { if(!st->dyndata #if USE_KIRA || !get_parti_time_range( st, tminp, tmaxp ) #endif ) { *tminp = 0; *tmaxp = st->ntimes == 0 ? 0 : st->ntimes - 1; } } int specks_get_datastep( struct stuff *st ) { return st->curtime; } double specks_get_realtime( struct stuff *st ) { return st->currealtime; } void set_interest_point( Point *p ) { #if CAVEMENU char cmd[80]; sprintf(cmd, "interest %g %g %g", p->x[0], p->x[1], p->x[2]); VIDI_queue_commandstr( cmd ); #else parti_center( p ); #endif } /* Only specks_timespecksptr() extends the spans of time or datasets */ struct specklist ** specks_timespecksptr( struct stuff *st, int dataset, int timestep ) { int d, needroom; struct specklist **na, **nan; void **ndf; char **nfn; if((timestep >= st->ntimes || dataset >= st->ndata)) { needroom = st->timeroom; if(timestep >= st->timeroom) needroom = 2*timestep + 15; for(d = 0; d < st->ndata || (dataset < MAXFILES && d <= dataset); d++) { if(needroom == st->timeroom && d < st->ndata) continue; na = NewN( struct specklist *, needroom ); nan = NewN( struct specklist *, needroom ); ndf = NewN( void *, needroom ); nfn = NewN( char *, needroom ); memset(na, 0, needroom * sizeof(*na)); memset(nan, 0, needroom * sizeof(*nan)); memset(ndf, 0, needroom * sizeof(*ndf)); memset(nfn, 0, needroom * sizeof(*nfn)); if(d < st->ndata && st->anima[d]) memcpy( na, st->anima[d], st->ntimes * sizeof(*na) ); if(d < st->ndata && st->annot[d]) memcpy( nan, st->annot[d], st->ntimes * sizeof(*nan) ); if(d < st->ndata && st->datafile[d]) memcpy( ndf, st->datafile[d], st->ntimes * sizeof(*ndf) ); if(d < st->ndata && st->fname[d]) memcpy( nfn, st->fname[d], st->ntimes * sizeof(*nfn) ); /* Don't free old pointers, just in case they're in use. */ st->anima[d] = na; st->annot[d] = nan; st->datafile[d] = ndf; st->fname[d] = nfn; } st->timeroom = needroom; if(timestep >= st->ntimes) st->ntimes = timestep + 1; if(dataset >= st->ndata && dataset < MAXFILES) st->ndata = dataset + 1; } return (timestep >= 0 && timestep < st->ntimes && dataset >= 0 && dataset < st->ndata) ? &st->anima[dataset][timestep] : NULL; } struct specklist * specks_timespecks( struct stuff *st, int dataset, int timestep ) { return (timestep >= 0 && timestep < st->ntimes && dataset >= 0 && dataset < st->ndata) ? st->anima[dataset][timestep] : NULL; } void specks_reupdate( struct stuff *st, struct specklist *sl ) { struct specklist *tsl; if(sl != NULL && sl->threshseq != st->threshseq) { for(tsl = sl; tsl != NULL; tsl = tsl->next) specks_rethresh( st, tsl, st->threshvar ); } if(sl != NULL && sl->colorseq != st->colorseq) { for(tsl = sl; tsl != NULL; tsl = tsl->next) specks_recolor( st, tsl, st->coloredby ); } if(sl != NULL && sl->sizeseq != st->sizeseq) { for(tsl = sl; tsl != NULL; tsl = tsl->next) specks_resize( st, tsl, st->sizedby ); } } void specks_set_time( struct stuff *st, double newtime ) { static Point lastinterest; clock_set_time( st->clk, newtime ); specks_set_timestep( st ); if(st->sl == NULL && st->ntimes > 1 && st->clk->parent == NULL) { /* Skip blank time-slots -- keep incrementing until either: * - we find a time-slot that has (or could have) some data, or * - we've run through all time-steps (avoid infinite loops!). */ int nudges, ts; for(nudges = 0; nudges < st->ntimes; nudges++) { ts = (st->curtime + nudges) % st->ntimes; if( specks_timespecks( st, st->curdata, ts ) != NULL || (st->datafile[st->curdata] != NULL && st->datafile[st->curdata][ts] != NULL) ) break; } clock_set_time( st->clk, ts ); specks_set_timestep( st ); } /* st->playnext = now + (st->fspeed != 0 ? 1/st->fspeed : 0); */ #if CAVE if(ment.tknob[0] == NULL) { /* if not SC99DEMO */ struct specklist *sl = st->sl; if(sl != NULL && memcmp(&sl->interest, &lastinterest, sizeof(Point)) && (sl->interest.x[0]!=0 || sl->interest.x[1]!=0 || sl->interest.x[2]!=0)) { char str[128]; float scale = .004; lastinterest = sl->interest; set_interest_point( &lastinterest ); sprintf(str, "\002setjump peak %g %g %g 0 0 0 %g", lastinterest.x[0], lastinterest.x[1]-scale*5, lastinterest.x[2] + scale*5, scale); VIDI_queue_commandstr( str ); } } #endif specks_reupdate( st, st->sl ); #if CAVE specks_refresh_menu( st ); #endif } static int specks_freenow( struct specklist **slp, int maxage ) { struct specklist *sl, **sprev; int any = 0; for(sprev = slp; (sl = *sprev) != NULL && sl->used <= maxage; ) { if(sl->used <= maxage) { *sprev = sl->freelink; if(sl->specks != NULL) Free(sl->specks); Free(sl); any++; } else { /* too recent - might still be in use */ sprev = &sl->freelink; } } return any; } void specks_discard( struct stuff *st, struct specklist **slp ) { struct specklist *sl, *slnext; for(sl = *slp; sl != NULL; sl = slnext) { slnext = sl->next; sl->freelink = st->scrap; st->scrap = sl; } *slp = NULL; } int specks_purge( void *vst, int nbytes, void *aarena ) { struct stuff *st = (struct stuff *)vst; int oldused = st->used; int oldtime = -1, oldds = -1; int t, ds; struct specklist *sl; #ifdef sgi static int first = 1; struct mallinfo mi; mi = amallinfo( aarena ); if(first) { first = 0; msg("Purging %dKbyte shmem arena (currently %dK used in %d blks, %dK free)\n", mi.arena>>10, mi.uordblks>>10, mi.ordblks, mi.fordblks>>10); } #endif /* Free any known scrap first */ #define OLD_ENOUGH 4 if(specks_freenow( &st->scrap, st->used - OLD_ENOUGH ) > 0) return 1; for(t = 0; t < st->ntimes; t++) { if(t == st->curtime) continue; for(ds = 0; ds < st->ndata; ds++) { sl = st->anima[ds][t]; if(sl != NULL && sl != st->sl && sl->used < oldused) { oldused = st->used; oldtime = t; oldds = ds; } } } if(oldtime >= 0) { specks_discard( st, &st->anima[oldds][oldtime] ); specks_freenow( &st->scrap, oldtime ); return 1; /* We freed something, so try allocating again */ } else { msg("Ran out of shmem, couldn't find anything more to purge\n"); #ifdef sgi msg("%dKbyte shmem arena (currently %dK used in %d blks, %dK free)\n", mi.arena>>10, mi.uordblks>>10, mi.ordblks, mi.fordblks>>10); #endif return 0; /* No progress made -- give up */ } } /* * Just stash these values in our frame function so they won't change visibly * during a frame. Each frame function will do this; we'll just hope that * they don't change as the various cave-wall processes start. */ void specks_current_frame( struct stuff *st, struct specklist *sl ) { st->frame_sl = sl; st->frame_time = st->curtime; st->frame_data = st->curdata; st->frame_annotation = st->annotation; specks_reupdate( st, sl ); } #define MAXXYFAN 16 extern void specks_draw_boxes( struct stuff *st, struct AMRbox *boxes, int levelmask, Matrix Ttext, int oriented ); struct cpoint { int rgba; Point p; }; void dumpcpoints( struct cpoint *cp, int n ) { while(--n >= 0) { glColor4ubv( (GLubyte *)&cp->rgba ); glVertex3fv( &cp->p.x[0] ); cp++; } } static Point depth_fwd; static float depth_d; struct order { float z; struct speck *sp; struct specklist *sl; }; static int depthcmp( const void *a, const void *b ) { return ((struct order *)a)->z < ((struct order *)b)->z ? 1 : ((struct order *)a)->z > ((struct order *)b)->z ? -1 : 0; } static int additive_blend; void sortedpolys( struct stuff *st, struct specklist *slhead, Matrix *Tc2wp, float radperpix, float polysize ) { struct speck *sp, *sbase; struct order *op, *obase; int i, k, total, skip; struct specklist *sl; int usethresh = st->usethresh&P_USETHRESH ? THRESHBIT : 0; int bps = 0; int prevrgba = -1; int usearea = st->polyarea; int sizevar = st->polysizevar; int polyorivar = st->polyorivar0; int texturevar = st->texturevar; int txno; int texturing = -1; float s; float polyminrad = st->polymin * radperpix; float polymaxrad = st->polymax * radperpix; float mins2d = polyminrad * polyminrad; int rgba; int alpha = st->alpha * 255; int nfan = st->npolygon<MAXXYFAN ? st->npolygon : MAXXYFAN; float xyfan[MAXXYFAN][2]; Point sfan[MAXXYFAN], pfan[MAXXYFAN]; Matrix Tc2w = *Tc2wp; float scl = vlength( (Point *)&Tc2w.m[0*4+0] ); float fanscale; Texture *wanttx; int additive = additive_blend; int wantblend = additive; int useclip = (st->clipbox.level > 0); Point clipp0 = st->clipbox.p0; Point clipp1 = st->clipbox.p1; for(total = 0, sl = slhead; sl != NULL; sl = sl->next) { if(sl->text != NULL || sl->nspecks == 0 || sl->special != SPECKS) continue; skip = st->subsample; if(sl->subsampled != 0) /* if already subsampled */ skip /= sl->subsampled; if(skip <= 0) skip = 1; if(bps < sl->bytesperspeck) bps = sl->bytesperspeck; if(usethresh) { for(i=sl->nspecks, sp=sl->specks; i>0; i-=skip, sp=NextSpeck(sp,sl,skip)) { if((usethresh & sp->rgba) == 0) total++; } } else { total += sl->nspecks / skip; } } obase = op = (struct order *)malloc( (total+1) * sizeof(struct order) ); for(sl = slhead; sl != NULL; sl = sl->next) { if(sl->text != NULL || sl->nspecks == 0 || sl->special != SPECKS) continue; skip = st->subsample; if(sl->subsampled != 0) /* if already subsampled */ skip /= sl->subsampled; if(skip <= 0) skip = 1; for(i=sl->nspecks, sp=sl->specks; i > 0; i-=skip, sp=NextSpeck(sp,sl,skip)) { float dist; if(usethresh & sp->rgba) continue; dist = VDOT( &sp->p, &depth_fwd ) + depth_d; if(dist < 0) continue; if(useclip && (sp->p.x[0] < clipp0.x[0] || sp->p.x[0] > clipp1.x[0] || sp->p.x[1] < clipp0.x[1] || sp->p.x[1] > clipp1.x[1] || sp->p.x[2] < clipp0.x[2] || sp->p.x[2] > clipp1.x[2])) continue; op->z = dist; op->sp = sp; op->sl = sl; op++; } } total = op - obase; qsort( obase, total, sizeof(*obase), depthcmp ); prevrgba = 0; /* Build prototype fan -- unit disk in screen plane */ /* Scale the fan big enough that the unit disk is inscribed in our polygon */ fanscale = 1 / (scl * cos(M_PI/nfan)); for(i = 0; i < nfan; i++) { float theta = 2*M_PI*(i+0.5f)/nfan; xyfan[i][0] = cos(theta); xyfan[i][1] = sin(theta); vcomb( &sfan[i], xyfan[i][0] * fanscale, (Point *)&Tc2w.m[0*4+0], xyfan[i][1] * fanscale, (Point *)&Tc2w.m[1*4+0] ); } if(st->usetextures == 0 || SMALLSPECKSIZE(texturevar) > bps) texturevar = -1; if(SMALLSPECKSIZE(polyorivar) > bps) polyorivar = -1; for(i = 0, op = obase; i < total; i++, op++) { float dist, size; sp = op->sp; dist = op->z; size = sp->val[sizevar] * polysize; if(usearea) { if(size < dist * dist * mins2d) continue; size = sqrtf(size); } else { if(size < dist * polyminrad) continue; } if(size > dist * polymaxrad) size = dist * polymaxrad; rgba = sp->rgba & ~THRESHBIT; if(rgba != prevrgba) { prevrgba = rgba; rgba = RGBALPHA( prevrgba, alpha ); glColor4ubv( (GLubyte *)&rgba ); } if(texturevar >= 0 && (txno = sp->val[texturevar]) >= 0 && txno < st->ntextures && (wanttx = st->textures[txno]) != NULL) { txbind( wanttx, &texturing ); wantblend = (wanttx->flags & TXF_ADD) ? 1 : additive_blend; } else if(texturing) { glDisable( GL_TEXTURE_2D ); texturing = 0; } if(wantblend != additive) { additive = wantblend; glBlendFunc( GL_SRC_ALPHA, additive ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA ); } if(polyorivar >= 0 && sp->val[polyorivar] < 9) { float *xv = &sp->val[polyorivar]; float *yv = &sp->val[polyorivar+3]; glBegin( GL_TRIANGLE_FAN ); if(texturing) { for(k = 0; k < nfan; k++) { glTexCoord2fv( &xyfan[k][0] ); glVertex3f( sp->p.x[0] + size*(xyfan[k][0]*xv[0] + xyfan[k][1]*yv[0]), sp->p.x[1] + size*(xyfan[k][0]*xv[1] + xyfan[k][1]*yv[1]), sp->p.x[2] + size*(xyfan[k][0]*xv[2] + xyfan[k][1]*yv[2])); } } else { for(k = 0; k < nfan; k++) { glVertex3f( sp->p.x[0] + size*(xyfan[k][0]*xv[0] + xyfan[k][1]*yv[0]), sp->p.x[1] + size*(xyfan[k][0]*xv[1] + xyfan[k][1]*yv[1]), sp->p.x[2] + size*(xyfan[k][0]*xv[2] + xyfan[k][1]*yv[2])); } } glEnd(); } else { glBegin( GL_TRIANGLE_FAN ); if(texturing) { for(k = 0; k < nfan; k++) { glTexCoord2fv( &xyfan[k][0] ); glVertex3f( sp->p.x[0] + size*sfan[k].x[0], sp->p.x[1] + size*sfan[k].x[1], sp->p.x[2] + size*sfan[k].x[2] ); } } else { for(k = 0; k < nfan; k++) { glVertex3f( sp->p.x[0] + size*sfan[k].x[0], sp->p.x[1] + size*sfan[k].x[1], sp->p.x[2] + size*sfan[k].x[2] ); } } glEnd(); } } free(obase); if(texturing > 0) txbind( NULL, NULL ); } void drawspecks( struct stuff *st ) { int i, slno, k; int rgba, alpha, prevrgba = 0; float prevsize = 0; struct specklist *sl, *slhead; register struct speck *p; Matrix Tw2c, Tc2w, Ttext, Tproj, Ttemp; static Point zero = {0,0,0}; int xywh[4]; float radperpix; Point tp, fan[MAXXYFAN]; Point eyepoint; Point fwd; float fwdd; float tscale, scl, fanscale; int skip; static int nxyfan = 0; static float xyfan[MAXXYFAN][2]; static unsigned char randskip[256]; int randix = 0; int fast = st->fast; int inpick = st->inpick; int usethresh = st->usethresh&P_USETHRESH ? THRESHBIT : 0; int fixeddist; float polyminrad, polymaxrad; float threshmin = st->thresh[0]; float threshmax = st->thresh[1]; int useclip = (st->clipbox.level > 0); Point clipp0 = st->clipbox.p0; Point clipp1 = st->clipbox.p1; float plum = st->psize; float knee1dist2 = st->fadeknee1 * st->fadeknee1; float knee2dist2 = st->fadeknee2 * st->fadeknee2; float orthodist2 = st->fadeknee2 * st->fadeknee2; float steep2knee2 = st->knee2steep * st->knee2steep / knee2dist2; float faderball2 = 1 / (st->fadeknee2 * st->fadeknee2); Point fadecen = st->fadecen; int fademodel = st->fade; if(!st->useme) return; switch(fademodel) { case F_CONSTANT: if(orthodist2 <= 0) orthodist2 = 1; break; case F_KNEE12: if(st->fadeknee1 >= st->fadeknee2 || st->fadeknee1 <= 0) fademodel = F_KNEE2; /* and fall into... */ case F_KNEE2: if(st->fadeknee2 <= 0) fademodel = F_SPHERICAL; break; case F_LREGION: if(st->fadeknee2 <= 0) faderball2 = 1; break; } { float r=0,g=0,b=0; /* Ugh. Allow background to be non-black */ sscanf(parti_bgcolor(NULL), "%f%f%f", &r,&g,&b); additive_blend = (r+g+b == 0); } if(st->clipbox.level != 0) { GLdouble plane[4]; if(st->clipbox.level == 1) { struct AMRbox b[2]; b[0] = st->clipbox; b[0].level = 0; b[1].level = -1; specks_draw_boxes( st, b, ~0, Tidentity, 1 ); } for(i = 0; i < 3; i++) { plane[0] = plane[1] = plane[2] = 0; plane[i] = 1; plane[3] = -st->clipbox.p0.x[i]; glClipPlane( GL_CLIP_PLANE0 + i, plane ); glEnable( GL_CLIP_PLANE0 + i ); plane[i] = -1; plane[3] = st->clipbox.p1.x[i]; glClipPlane( GL_CLIP_PLANE0 + 3 + i, plane ); glEnable( GL_CLIP_PLANE0 + 3 + i ); } } if(nxyfan != st->npolygon && st->npolygon > 0) { if(st->npolygon > MAXXYFAN) st->npolygon = MAXXYFAN; nxyfan = st->npolygon; fanscale = 1 / cos(M_PI/nxyfan); for(i = 0; i < nxyfan; i++) { float th = (i+.5f)*2*M_PI / nxyfan; xyfan[i][0] = cos(th) * fanscale; xyfan[i][1] = sin(th) * fanscale; } srandom(11); for(i = 0; i < 256; i++) randskip[i] = random() & 0xFF; } alpha = st->alpha * 255; rgba = RGBALPHA( RGBWHITE, alpha ); /* BIG-ENDIAN (1,1,1,alpha) */ /* Find displacements which lie in the screen plane, for making * billboard-style polygonal patches, and for text. */ glGetFloatv( GL_MODELVIEW_MATRIX, Tw2c.m ); eucinv( &Tc2w, &Tw2c ); scl = vlength( (Point *)&Tw2c.m[0] ); for(i = 0; i < nxyfan; i++) { tp.x[0] = scl*st->polysize*xyfan[i][0]; tp.x[1] = scl*st->polysize*xyfan[i][1]; tp.x[2] = 0; vtfmvector( &fan[i], &tp, &Tc2w ); } /* Find projection matrix and screen (well, viewport) size, * so we can convert angular sizes to screen (pixel) sizes, * in radians per pixel. */ glGetFloatv( GL_PROJECTION_MATRIX, Tproj.m ); glGetIntegerv( GL_VIEWPORT, xywh ); radperpix = 1 / (.5*xywh[2] * Tproj.m[0*4+0]); /* Construct a "forward" vector in object coords too, for measuring * distance from camera plane. Note camera looks toward its -Z axis not +Z! */ tp.x[0] = 0, tp.x[1] = 0, tp.x[2] = -1; vtfmvector( &fwd, &tp, &Tc2w ); vunit( &fwd, &fwd ); /* * Actually we want a plane equation, whose value is zero in the * eye plane. Camera-space distance from camera plane = * vdot( &objectpoint, &fwd ) + fwdd. */ vtfmpoint( &eyepoint, &zero, &Tc2w ); fwdd = -vdot( &eyepoint, &fwd ); tscale = scl * st->textsize; mcopy( &Ttemp, &Tidentity ); Ttemp.m[0*4+0] = Ttemp.m[1*4+1] = Ttemp.m[2*4+2] = tscale; mmmul( &Ttext, &Ttemp, &Tc2w ); vsettranslation( &Ttext, &zero ); glDisable( GL_LIGHTING ); /* Draw any boxes (even if we have no specks) for this timestep */ if(st->useboxes && st->boxlevelmask != 0 && st->frame_time >= 0 && st->frame_time < st->boxtimes && st->boxes[st->frame_time] != NULL) { specks_draw_boxes( st, st->boxes[st->frame_time], st->boxlevelmask, Ttext, 0 ); } if(st->useboxes && st->staticboxes != NULL) { specks_draw_boxes( st, st->staticboxes, st->boxlevelmask, Ttext, st->boxaxes ); } slhead = st->frame_sl; /* st->sl as snapped by specks_ffn() */ if(slhead == NULL) slhead = st->sl; /* maybe there is no specks_ffn() */ if(slhead == NULL) return; #if USE_KIRA kira_draw( st, slhead, &Tc2w, radperpix ); #endif skip = st->subsample; if(slhead->subsampled != 0) /* if already subsampled */ skip /= slhead->subsampled; if(skip == 0) skip = 1; for(sl = slhead; sl != NULL; sl = sl->next) sl->used = st->used; if((unsigned int)st->sizedby <= MAXVAL && (unsigned int)st->curdata < MAXFILES && st->vdesc[st->curdata][st->sizedby].lum != 0) { plum *= st->vdesc[st->curdata][st->sizedby].lum; } if(st->subsample > 0 && st->everycomp) plum *= st->subsample; /* Compensate for "every" subsampling */ if(st->alpha >= 1) { glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); } else { glEnable(GL_BLEND); glBlendFunc( GL_SRC_ALPHA, additive_blend ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA ); glEnable(GL_DEPTH_TEST); glDepthMask( GL_FALSE ); } if(inpick) glLoadName(0); if(st->usepoly && st->polysize > 0) { int texturing = 0; int texturevar = st->usetextures && st->texturevar >= 0 && st->texturevar < MAXVAL ? st->texturevar : -1; int usearea = st->polyarea; int sizevar = st->polysizevar; float polysize = st->polysize; float mins2d; int txno; glMatrixMode( GL_TEXTURE ); glLoadIdentity(); glTranslatef( .5, .5, 0 ); glScalef( st->txscale, st->txscale, st->txscale ); glMatrixMode( GL_MODELVIEW ); polyminrad = st->polymin * radperpix; polymaxrad = st->polymax * radperpix; mins2d = polyminrad*polyminrad; if(sizevar == -1) { /* If polygon size is tied to point size, * then include pointsize scale factors in polygon scaling. */ if((unsigned int)st->sizedby <= MAXVAL && (unsigned int)st->curdata < MAXFILES && st->vdesc[st->curdata][st->sizedby].lum != 0) polysize *= st->vdesc[st->curdata][st->sizedby].lum; if(st->subsample > 0 && st->everycomp) polysize *= st->subsample; /* Compensate for "every" subsampling */ } if(st->depthsort && !inpick) { depth_fwd = fwd; depth_d = fwdd; sortedpolys( st, slhead, &Tc2w, radperpix, polysize ); } else { for(sl = slhead, slno = 1; sl != NULL; sl = sl->next, slno++) { if(sl->text != NULL || sl->special != SPECKS) continue; if(inpick) { glLoadName(slno); glPushName(0); } for(i = 0, p = sl->specks; i < sl->nspecks; i+=skip, p = NextSpeck( p, sl, skip )) { float dist = VDOT( &p->p, &fwd ) + fwdd; float size; if(dist <= 0) continue; if(usethresh & p->rgba) continue; size = p->val[sizevar] * polysize; if(usearea) { if(size < dist * dist * mins2d) continue; size = sqrtf(size); } else { if(size < dist * polyminrad) continue; } if(size > dist * polymaxrad) size = dist * polymaxrad; rgba = p->rgba & ~THRESHBIT; if(rgba != prevrgba) { prevrgba = rgba; rgba = RGBALPHA( prevrgba, alpha ); glColor4ubv( (GLubyte *)&rgba ); } if(st->polyorivar0 >= 0 && p->val[st->polyorivar0] < 9) { for(k = 0; k < nxyfan; k++) { vcomb( &fan[k], size*xyfan[k][0], (Point *)&p->val[st->polyorivar0], size*xyfan[k][1], (Point *)&p->val[st->polyorivar0+3] ); } } else if(p->size != prevsize) { float s = scl*size; for(k = 0; k < nxyfan; k++) { vcomb( &fan[k], s*xyfan[k][0], (Point *)&Tc2w.m[0*4+0], s*xyfan[k][1], (Point *)&Tc2w.m[1*4+0] ); } prevsize = size; } #define PFAN(vno, comp) p->p.x[comp] + fan[vno].x[comp] if(inpick) { glLoadName( i ); glBegin( GL_TRIANGLE_FAN ); for(k = 0; k < nxyfan; k++) { glVertex3f( PFAN(k,0), PFAN(k,1), PFAN(k,2) ); } glEnd(); } else if(texturevar >= 0 && (txno = p->val[texturevar]) >= 0 && txno < st->ntextures && st->textures[txno] != NULL) { txbind( st->textures[txno], &texturing ); glBegin( GL_TRIANGLE_FAN ); for(k = 0; k < nxyfan; k++) { glTexCoord2fv( &xyfan[k][0] ); glVertex3f( PFAN(k,0), PFAN(k,1), PFAN(k,2) ); } glEnd(); } else { if(texturing) { texturing = 0; glDisable( GL_TEXTURE_2D ); } glBegin(GL_TRIANGLE_FAN); for(k = 0; k < nxyfan; k++) glVertex3f( PFAN(k,0), PFAN(k,1), PFAN(k,2) ); glEnd(); } #undef PFAN } if(inpick) glPopName(); } } if(texturing) { txbind( NULL, NULL ); glDisable( GL_TEXTURE_2D ); } glMatrixMode( GL_TEXTURE ); glLoadIdentity(); glMatrixMode( GL_MODELVIEW ); } if(st->usepoint && !(st->useboxes == 2)) { #define MAXPTSIZE 16 /* in half-point units */ #define PERBUCKET 64 /* max points per bucket */ struct cpoint sized[MAXPTSIZE*2][PERBUCKET]; int nsized[MAXPTSIZE*2]; unsigned char invgamma[256]; float invgam = (st->gamma <= 0) ? 0 : 1/st->gamma; for(i = 0; i < 256; i++) invgamma[i] = (int) (255.99 * pow( i/255., invgam )); if(inpick) { for(sl = slhead, slno = 1; sl != NULL; sl = sl->next, slno++) { if(sl->text != NULL || sl->special != SPECKS) continue; glLoadName(slno); glPushName(0); for(i = 0, p = sl->specks; i < sl->nspecks; i+=skip, p = NextSpeck(p, sl, skip)) { if(usethresh & p->rgba) continue; glLoadName(i); glBegin(GL_POINTS); glVertex3fv( &p->p.x[0] ); glEnd(); } glPopName(); } } else if(fast) { static unsigned char apxsize[MAXPTSIZE*MAXPTSIZE]; unsigned char faintrand[256]; int pxsize, oldpxsize; int pxmin, pxmax; pxmin = 256 * st->pfaint; if(st->plarge > MAXPTSIZE) st->plarge = MAXPTSIZE; pxmax = 256 * st->plarge * st->plarge; if(apxsize[1] == 0) { for(i=0; i<COUNT(apxsize); i++) apxsize[i] = (int)ceil(sqrtf(i+1)); } for(i = 0; i < 256; i++) faintrand[i] = randskip[i] * st->pfaint; /* Render using fast (non-antialiased) points */ glDisable( GL_POINT_SMOOTH ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, additive_blend ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA ); prevrgba = 0; prevsize = 0; sl = slhead; pxsize = oldpxsize = 1; glPointSize( pxsize ); glColor4ubv( (GLubyte *)&rgba ); for(i = 0; i < MAXPTSIZE*2; i++) nsized[i] = 0; glBegin( GL_POINTS ); for(sl = slhead; sl != NULL; sl = sl->next) { if(sl->text != NULL || sl->special != SPECKS) continue; for(i = 0, p = sl->specks; i < sl->nspecks; i+=skip, p= NextSpeck(p, sl, skip)) { int lum, myalpha; float dist = VDOT( &p->p, &fwd ) + fwdd; if(dist <= 0) /* Behind eye plane */ continue; if(usethresh & p->rgba) continue; if(useclip && (p->p.x[0] < clipp0.x[0] || p->p.x[0] > clipp1.x[0] || p->p.x[1] < clipp0.x[1] || p->p.x[1] > clipp1.x[1] || p->p.x[2] < clipp0.x[2] || p->p.x[2] > clipp1.x[2])) continue; lum = 256 * plum * p->size / (dist*dist); if(lum < pxmin) { if(lum <= faintrand[(i*i+i) /*randix++*/ & 0xFF]) continue; pxsize = 1; myalpha = pxmin; } else if(lum < 256) { pxsize = 1; myalpha = lum; } else if(lum < pxmax) { pxsize = apxsize[lum>>8]; myalpha = lum / (pxsize*pxsize); } else { /* Could use a polygon here, instead. */ pxsize = st->plarge; myalpha = 255; } if(pxsize < 1 || pxsize > 6 || myalpha <= 0 || myalpha > 255) { static int oops; oops++; } if(myalpha < 0 || myalpha > 255 || invgamma[myalpha] <= 0 || invgamma[myalpha] > 255) { static int oops2; oops2++; } rgba = RGBALPHA( p->rgba&~THRESHBIT, invgamma[myalpha] & 0xFC ); if(pxsize != oldpxsize) { if(nsized[pxsize] >= PERBUCKET) { glEnd(); glPointSize(pxsize); glBegin( GL_POINTS ); dumpcpoints( &sized[pxsize][0], PERBUCKET ); nsized[pxsize] = 0; oldpxsize = pxsize; glColor4ubv( (GLubyte *)&rgba ); prevrgba = rgba; glVertex3fv( &p->p.x[0] ); } else { struct cpoint *cp = &sized[pxsize][nsized[pxsize]]; cp->rgba = rgba; cp->p = p->p; nsized[pxsize]++; } } else { /* same as current pointsize */ if(rgba != prevrgba) { glColor4ubv( (GLubyte *)&rgba ); prevrgba = rgba; } glVertex3fv( &p->p.x[0] ); } } } glEnd(); for(i = 0; i < MAXPTSIZE; i++) { if(nsized[i] > 0) { glPointSize( i ); glBegin( GL_POINTS ); dumpcpoints( &sized[i][0], nsized[i] ); glEnd(); } } } else { /* * Render using anti-aliased points */ static unsigned char apxsize[(MAXPTSIZE*2)*(MAXPTSIZE*2)]; unsigned char faintrand[256]; static float percoverage[MAXPTSIZE*2]; static int needMesaHack = 0; int pxsize, oldpxsize; int pxmin, pxmax; int minsize, minalpha; pxmin = (256 * (2*2)) * st->pfaint; if(st->plarge > MAXPTSIZE*2) st->plarge = MAXPTSIZE*2; pxmax = (256 * (2*2)) * st->plarge * st->plarge; if(apxsize[1] == 0) { char *version = (char *)glGetString( GL_VERSION ); /* Hack: MESA doesn't handle small anti-aliased points well; * force them all to be of size 1.0 or 2.0 (i.e. pxsize == 2 or 4), * and non-antialiased. */ if(getenv("MESAHACK") != NULL) needMesaHack = atoi(getenv("MESAHACK")); else if(version && (!strncmp(version, "CRIME", 5) || !strncmp(version, "IR", 2))) needMesaHack = 0; /* O2 & IR: nice anti-aliased points */ else needMesaHack = -1; /* IMPACT (& others?): ugly AA points */ for(i=0; i<COUNT(apxsize); i++) { apxsize[i] = (int)ceil(sqrtf(i+1)); if(needMesaHack>0) { if(apxsize[i] <= 2) apxsize[i] = 2; else if(needMesaHack<2 && apxsize[i] <= 4) apxsize[i] = 4; } } for(i = 1; i < MAXPTSIZE*2; i++) { percoverage[i] = 1.0 / (i*i); if(needMesaHack>0) { if(i <= 2) percoverage[i] = .25; else if(needMesaHack<2 && i <= 4) percoverage[i] = .0625; } } percoverage[0] = 1.0; if(needMesaHack < 0) percoverage[1] = 0.25; /* if gfx treats psize 0.5 == 1.0 */ } minsize = apxsize[ pxmin >> 8 ]; minalpha = pxmin * percoverage[ minsize ]; for(i = 0; i < 256; i++) faintrand[i] = randskip[i] * st->pfaint; /* Render using antialiased points */ glEnable( GL_POINT_SMOOTH ); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, additive_blend ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA ); prevrgba = 0; prevsize = 0; sl = slhead; pxsize = 2; oldpxsize = 99; glPointSize( pxsize ); glColor4ubv( (GLubyte *)&rgba ); for(i = 0; i < MAXPTSIZE*2; i++) nsized[i] = 0; glBegin( GL_POINTS ); for(sl = slhead; sl != NULL; sl = sl->next) { if(sl->text != NULL || sl->special != SPECKS) continue; for(i = 0, p = sl->specks; i < sl->nspecks; i+=skip, p=NextSpeck(p,sl,skip)) { int lum, myalpha; float dist, dist2, dx, dy, dz; if(usethresh & p->rgba) continue; if(useclip && (p->p.x[0] < clipp0.x[0] || p->p.x[0] > clipp1.x[0] || p->p.x[1] < clipp0.x[1] || p->p.x[1] > clipp1.x[1] || p->p.x[2] < clipp0.x[2] || p->p.x[2] > clipp1.x[2])) continue; switch(fademodel) { case F_PLANAR: dist = VDOT( &p->p, &fwd ) + fwdd; if(dist <= 0) /* Behind eye plane */ continue; dist2 = dist*dist; break; case F_CONSTANT: dist2 = orthodist2; break; case F_SPHERICAL: dx = p->p.x[0]-eyepoint.x[0]; dy = p->p.x[1]-eyepoint.x[1]; dz = p->p.x[2]-eyepoint.x[2]; dist2 = dx*dx + dy*dy + dz*dz; break; case F_LREGION: /* not impl yet */ dist = VDOT( &p->p, &fwd ) + fwdd; if(dist <= 0) /* Behind eye plane */ continue; dx = p->p.x[0] - fadecen.x[0]; dy = p->p.x[1] - fadecen.x[1]; dz = p->p.x[2] - fadecen.x[2]; dist2 = dist * st->fadeknee2 / (1 + (dx*dx + dy*dy + dz*dz) * faderball2); break; case F_LINEAR: dist = VDOT( &p->p, &fwd ) + fwdd; if(dist <= 0) /* Behind eye plane */ continue; dist2 = dist * st->fadeknee2; break; case F_KNEE2: dx = p->p.x[0]-eyepoint.x[0]; dy = p->p.x[1]-eyepoint.x[1]; dz = p->p.x[2]-eyepoint.x[2]; dist2 = dx*dx + dy*dy + dz*dz; if(dist2 > knee2dist2) dist2 *= 1 + steep2knee2 * (dist2 - knee2dist2); break; case F_KNEE12: dx = p->p.x[0]-eyepoint.x[0]; dy = p->p.x[1]-eyepoint.x[1]; dz = p->p.x[2]-eyepoint.x[2]; dist2 = dx*dx + dy*dy + dz*dz; if(dist2 < knee1dist2) dist2 = knee1dist2; else if(dist2 > knee2dist2) dist2 *= 1 + steep2knee2 * (dist2 - knee2dist2); break; } lum = (256 * (2*2)) * plum * p->size / dist2; if(lum <= pxmin) { if(lum <= faintrand[(i*i+i) /*randix++*/ & 0xFF]) continue; pxsize = minsize; myalpha = minalpha; } else if(lum < pxmax) { pxsize = apxsize[lum>>8]; myalpha = lum * percoverage[pxsize]; } else { /* Could use a polygon here, instead. */ pxsize = 2 * st->plarge; myalpha = 255; } if(pxsize < 1 || pxsize >= MAXPTSIZE*2 || myalpha <= 0 || myalpha > 255) { static int oops; oops++; pxsize = MAXPTSIZE*2 - 1; myalpha = 255; } rgba = RGBALPHA( p->rgba&~THRESHBIT, invgamma[myalpha] & 0xFC ); if(pxsize != oldpxsize) { if(nsized[pxsize] >= PERBUCKET) { glEnd(); if(needMesaHack>0 && (pxsize <= 4) != (oldpxsize <= 4)) { if(pxsize <= 4) glDisable( GL_POINT_SMOOTH ); else glEnable( GL_POINT_SMOOTH ); } glPointSize(.5*pxsize); glBegin( GL_POINTS ); dumpcpoints( &sized[pxsize][0], PERBUCKET ); nsized[pxsize] = 0; oldpxsize = pxsize; glColor4ubv( (GLubyte *)&rgba ); prevrgba = rgba; glVertex3fv( &p->p.x[0] ); } else { struct cpoint *cp = &sized[pxsize][nsized[pxsize]]; cp->rgba = rgba; cp->p = p->p; nsized[pxsize]++; } } else { /* same as current pointsize */ if(rgba != prevrgba) { glColor4ubv( (GLubyte *)&rgba ); prevrgba = rgba; } glVertex3fv( &p->p.x[0] ); } } } glEnd(); if(needMesaHack>0) glDisable( GL_POINT_SMOOTH ); for(i = 0; i < MAXPTSIZE*2; i++) { if(needMesaHack>0 && i == 4) glEnable( GL_POINT_SMOOTH ); if(nsized[i] > 0) { glPointSize( .5*i ); glBegin( GL_POINTS ); dumpcpoints( &sized[i][0], nsized[i] ); glEnd(); } } } } if(st->usetext && st->textsize != 0) { int cment = -1; glColor3f(1,1,1); for(sl = slhead, slno = 1; sl != NULL; sl = sl->next, slno++) { if(sl->text != NULL && (p = sl->specks) != NULL) { float dist = VDOT( &p->p, &fwd ) + fwdd; float tsize; if(dist <= 0) continue; if(inpick) glLoadName(slno); tsize = st->textsize * p->size; if(tsize < dist * (radperpix * st->textmin)) { /* Could draw a stub here -- a simple line segment of * about the right length */ Point ep; /* extract top row of Ttext -- this is the unit screen-space X * vector, expressed in world coords. Scale to suit. */ vcomb( &ep, p->size * sfStrWidth(sl->text), (Point *)&Ttext.m[0], 1, &p->p ); if(p->rgba != cment) { if((unsigned int)p->rgba >= st->textncmap) glColor3f( 1, 1, 1 ); else glColor3ubv( (GLubyte *)&st->textcmap[ p->rgba ].cooked ); cment = p->rgba; } glBegin( GL_LINES ); glVertex3fv( p->p.x ); glVertex3fv( ep.x ); glEnd(); continue; } /* Otherwise, it's big enough to see -- draw actual text */ glPushMatrix(); glTranslatef( p->p.x[0], p->p.x[1], p->p.x[2] ); if(st->usetextaxes) { static unsigned char col[3][3] = {255,0,0, 0,255,0, 0,0,255}; static float pos[3][3] = {1,0,0, 0,1,0, 0,0,1}; glPushMatrix(); glScalef( tsize, tsize, tsize ); glBegin(GL_LINES); for(k = 0; k < 3; k++) { glColor3ubv( &col[k][0] ); glVertex3f(0,0,0); glVertex3fv( &pos[k][0] ); } glEnd(); glPopMatrix(); } if(p->rgba != cment || st->usetextaxes) { if((unsigned int)p->rgba >= st->textncmap) glColor3f( 1, 1, 1 ); else glColor3ubv( (GLubyte *)&st->textcmap[ p->rgba ].cooked ); cment = p->rgba; } glMultMatrixf( Ttext.m ); sfStrDraw( sl->text, p->size, NULL ); glPopMatrix(); } } } if(inpick) glLoadName(0); for(i = 0; i < 6; i++) /* in case clipbox was enabled when we began */ glDisable( GL_CLIP_PLANE0 + i ); glColor4ub(255,255,255,255); glDisable( GL_BLEND ); glEnable( GL_DEPTH_TEST ); /* was already enabled */ glDepthMask( GL_TRUE ); } void specks_draw_ellipsoids( struct stuff *st, struct specklist *asl ) { struct specklist *sl; struct speck *sp; for(sl = asl; sl != NULL; sl = sl->next) { if(sl->special != ELLIPSOIDS || sl->text != NULL) continue; } } void specks_draw_boxes( struct stuff *st, struct AMRbox *boxes, int boxlevelmask, Matrix Ttext, int oriented ) { static short arcs[] = { 5, 4, 6, 2, 3, 1, 5, 7, 6, -1, 7, 3, 0, 1, 0, 2, 0, 4 }; struct AMRbox *box; int i; int blevels = (st->boxlevels > 1 ? st->boxlevels-1 : 1); float boxscale, s0, s1; int isscaled; glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glLineWidth( st->boxlinewidth ); glEnable( GL_BLEND ); glDisable( GL_LINE_SMOOTH ); /* Scan through the array, whose end is marked with a box at level -1. */ for(box = boxes; box->level >= 0; box++) { boxscale = (box->level < 0) ? 1.0 : st->boxscale[ box->level>=MAXBOXLEV ? MAXBOXLEV-1 : box->level ]; isscaled = (boxscale != 0 && boxscale != 1); if(isscaled) { s0 = .5*(1 + boxscale); s1 = .5*(1 - boxscale); } if((1 << box->level) & boxlevelmask) { int cval = RGBALPHA(st->boxcmap[ box->level<0 ? 0 : box->level>=st->boxncmap ? st->boxncmap-1 : box->level ].cooked, 0xFF); int colorme = 0; if(oriented) cval = orientboxcolor; glColor4ubv( (GLubyte *)&cval ); glBegin( GL_LINE_STRIP ); for(i = 0; i < COUNT(arcs); i++) { int vert = arcs[i]; if(vert < 0) { glEnd(); glBegin( GL_LINES ); } else { if(oriented) { if(vert == 0) { glColor4ubv( (GLubyte *)&cval ); colorme = 1; } else if(colorme) { glColor3f( vert&1?1:0, vert&2?1:0, vert&4?1:0 ); } } if(isscaled && !oriented && box->level >= 0) { glVertex3f( vert&1 ? s0*box->p0.x[0] + s1*box->p1.x[0] : s1*box->p0.x[0] + s0*box->p1.x[0], vert&2 ? s0*box->p0.x[1] + s1*box->p1.x[1] : s1*box->p0.x[1] + s0*box->p1.x[1], vert&4 ? s0*box->p0.x[2] + s1*box->p1.x[2] : s1*box->p0.x[2] + s0*box->p1.x[2] ); } else { glVertex3f( (vert&1 ? box->p1.x : box->p0.x)[0], (vert&2 ? box->p1.x : box->p0.x)[1], (vert&4 ? box->p1.x : box->p0.x)[2] ); } } } glEnd(); if(st->boxlabels && st->boxlabelscale != 0) { float sz = .16 * vdist( &box->p0, &box->p1 ) / st->textsize; char lbl[16]; glPushMatrix(); glTranslatef( .75*box->p0.x[0] + .25*box->p1.x[0], .75*box->p0.x[1] + .25*box->p1.x[1], .75*box->p0.x[2] + .25*box->p1.x[2]); glMultMatrixf( Ttext.m ); sprintf(lbl, "%d", box->boxno); sfStrDraw( lbl, sz, NULL ); glPopMatrix(); } } } glLineWidth( 1 ); } int specks_partial_pick_decode( struct stuff *st, int id, int nhits, int nents, GLuint *hitbuf, unsigned int *bestzp, struct specklist **slp, int *specknop, Point *pos ) { int i, hi, ns, slno; GLuint z0, bestz = *bestzp; int bestslno = 0, bestspeck = -1; struct specklist *sl, *slhead; if(st == NULL) return 0; 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(st->usepoly>1) /* debug */ printf(ns>1?"[%x %d/%d]":"[%x %d]", z0, hitbuf[i+3],hitbuf[i+4]); if(id == hitbuf[i+3] && bestz > z0 && ns > 1 && (slno = hitbuf[i+4]) > 0) { bestz = z0; bestslno = slno; bestspeck = (ns>2) ? hitbuf[i+5] : 0; } } if(bestslno <= 0) return 0; slhead = st->frame_sl; /* st->sl as snapped by specks_ffn() */ if(slhead == NULL) slhead = st->sl; /* maybe there is no specks_ffn() */ if(slhead == NULL) 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; } #if CAVE void parti_seto2w( struct stuff *st, int objno, CONST Matrix *newTo2w ) { st->objTo2w = *newTo2w; /* ignores objno -- there's only one right now! */ } void parti_geto2w( struct stuff *st, int objno, Matrix *curTo2w ) { *curTo2w = st->objTo2w; /* ignores objno -- there's only one right now! */ } void specks_display( struct stuff *st ) { int showmenus; int menuhit = 0; int empty = st->ntimes == 0 && st->sl == NULL; static int hidescene; if( CAT_dsp_mode == APP_DSP_NORMAL ) { #if !(MENU_IN_PPR) if( CAVEEye == CAVE_LEFT_EYE ) { menu_check( pmenu, pmenu->cavewall ); menu_check( stubmenu, stubmenu->cavewall ); menu_sethidden( pmenu, st->hidemenu || empty ); menu_sethidden( stubmenu, st->hidemenu<=0 || empty ); } #endif glPushMatrix(); /* The following only works if our transformation is exactly the * Cave nav transform, i.e. if the CAT system hasn't * translated us further; but that's true for this app. */ CAVENavInverseTransform(); menu_draw( pmenu ); if(!pmenu->hidden) { menuhit = (pmenu->wallhit > 0); menu_setpos( stubmenu, ment.menu->x0, ment.menu->y0 ); } menu_draw( stubmenu ); glPopMatrix(); } if(hidescene && !menuhit) hidescene = 0; if(menuhit && *CAVEFramesPerSecond < st->menudemandfps) hidescene = 1; /* Don't draw scene if too slow to operate menu easily */ if(hidescene) return; glPushMatrix(); glTranslatef( st->gtrans.x[0], st->gtrans.x[1], st->gtrans.x[2] ); glScalef( st->gscale, st->gscale, st->gscale ); glMultMatrixf( st->objTo2w.m ); drawspecks( st ); glPopMatrix(); } #endif /*NOCAVE*/ 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->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); } 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; } static float speckscale = 1; void specks_read( struct stuff **stp, char *fname ) { FILE *f; char line[2048], oline[2048]; char *key; struct stuff *st = *stp; int maxfields = 0; #define SPECKCHUNK 2001 /* should be bigger, but too many machines have tiny stack areas! */ struct speck sp[SPECKCHUNK]; struct speck s; int i, nsp; int ignorefirst = 0; int lno = 0; if(fname == NULL) return; key = NewA( char, strlen(fname)+1 ); /* fname's space might get reused */ strcpy(key, fname); fname = key; if((f = fopen(fname, "r")) == NULL) { msg("%s: can't open: %s", fname, strerror(errno)); return; } s.rgba = 0; s.size = 1; nsp = 0; #define SPFLUSH() \ if(nsp>0) { \ addchunk( st, nsp, sizeof(struct speck), \ speckscale, sp, NULL, \ maxfields > MAXVAL ? sizeof(struct speck) \ : SMALLSPECKSIZE( maxfields ) ); \ nsp = maxfields = 0; \ } 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); } strcpy(oline, line); key = strtok(line, separ); if(key == NULL || key[0] == '#' || key[0] == '\0') continue; if(nsp >= SPECKCHUNK || (isalnum(key[0]) && !isdigit(key[0]))) { SPFLUSH(); } if(!strcmp(key, "include") || !strcmp(key, "read")) { float oldscale = speckscale; char *infname = strtok(NULL, separ); 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; #ifdef USE_IEEEIO } else if(!strcmp(key, "ieee")) { int timestepno = st->datatime; char *realfile; char *s = strtok(NULL, separ); if(s != NULL && !strcmp(s, "-t")) { s = strtok(NULL, separ); if(s == NULL || (timestepno = getfloat(s, st->datatime)) < 0) { msg("ieee -t: expected timestepnumber(0-based), not %s", s); continue; } s = strtok(NULL, separ); } realfile = findfile( fname, s ); if(realfile == NULL) { msg("%s: ieee: can't find file %s", fname, s); } else { /* Load IEEE dataset into time slot */ specks_ieee_open( st, realfile, st->curdata, timestepno ); } #endif } else if(!strcmp(key, "object")) { key = strtok(NULL, separ); if(key) { char *eq = strchr(key, '='); parti_object( key, &st ); if(eq) { parti_set_alias( st, eq+1 ); } else { key = strtok(NULL, separ); if(key) { parti_set_alias( st, key[0]=='=' ? strtok(NULL, separ) : key ); } } } } else if(!strcmp(key, "kira")) { char *s = strtok(NULL, separ); char *realfile = findfile( fname, s ); char *verbose = strtok(NULL, separ); #ifndef USE_KIRA msg("Need to recompile with -DUSE_KIRA"); #else st->clk->continuous = 1; open_parti(st, realfile, verbose ? atoi(verbose) : 0); #endif } else if(!strcmp(key, "sdb")) { char *s = strtok(NULL, separ); int tno = st->datatime; char *realfile; if(s != NULL && !strcmp(s, "-t")) { if((tno = getfloat(s=strtok(NULL, separ), st->datatime)) < 0) { msg("sdb -t: expected timestepnumber(0-based), not %s", s); continue; } s = strtok(NULL, separ); } realfile = findfile( fname, s ); if(realfile == NULL) { msg("%s: sdb: can't find file %s", fname, s); } else { specks_read_sdb( st, realfile, tno ); } } else if(!strcmp(key, "sdbvars")) { char *s = strtok(NULL, separ); if(s != NULL) { if(s[strspn(s, "mMcrogtxyzSn")] != '\0') { msg("sdbvars: pattern must contain only chars from: mMcrogtxyzSn, not %s", s); } else { if(st->sdbvars) Free(st->sdbvars); st->sdbvars = shmstrdup(s); } } else { msg("sdbvars %s", st->sdbvars); } } else if(!strcmp(key, "box") || !strcmp(key, "boxes")) { char *args[16], **av; Point cen, rad; struct AMRbox box; int ac, k; int tno = -1; for(ac = 0; ac < COUNT(args) && (args[ac] = strtok(NULL, separ)) != NULL; ac++) ; box.boxno = -1; box.level = 0; av = args; while(ac > 2 && av[0][0] == '-') { if(av[0][1] == 'n' && sscanf(av[1], "%d", &box.boxno)>0) ac -= 2, av += 2; else if(av[0][1] == 'l' && sscanf(av[1], "%d", &box.level)>0) ac -= 2, av += 2; else if(av[0][1] == 't' && (tno = getfloat(av[1], st->datatime)) >= 0) ac -= 2, av += 2; else break; } if(ac == 2 && 3==sscanf(av[0], "%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2]) && 0<(k=sscanf(av[1], "%f%*c%f%*c%f", &rad.x[0],&rad.x[1],&rad.x[2]))) { 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(ac == 3 && 2==sscanf(av[0], "%f%*c%f",&box.p0.x[0],&box.p1.x[0]) && 2==sscanf(av[1], "%f%*c%f",&box.p0.x[1],&box.p1.x[1]) && 2==sscanf(av[2], "%f%*c%f",&box.p0.x[2],&box.p1.x[2])) { specks_add_box( st, &box, tno ); } else if(ac == 6) { for(k=3; --k >= 0; ) { if(sscanf(av[k], "%f", &box.p0.x[k])<=0 || sscanf(av[k+3], "%f", &box.p1.x[k])<=0) break; } if(k<0) 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(ac == 1) { char *realfile = findfile( fname, av[0] ); if(realfile == NULL) { msg("%s: boxes: can't find file %s", fname, av[0]); } else { specks_read_boxes( st, realfile, tno ); } } else { msg("usage: box [-t timestep] AMRboxfile -or-"); msg(" box [-t time] [-n boxno] [-l level] xcen,ycen,zcen xradius,yradius,zradius -or-"); msg(" box [options] xmin ymin zmin xmax ymax zmax"); msg(" options: -n <boxno> box number, for \"gobox\" and \"boxlabel\" cmds (dflt -1)"); msg(" -l <boxlevel> level-number (0..31) for \"showboxlevel\" cmds (dflt 0)"); msg(" -t <time> timestep, if animated; default eternal,"); msg(" or (for animated AMRboxfile) start time 0"); } } else if(!strncmp(key, "annot", 5)) { static char ausage[] = "Usage: annot [-t timestep] string..."; char *es, *s = strtok(NULL, "\r\n"); int tno = st->datatime; if(s == NULL) { msg(ausage); continue; } while(*s && isspace(*s)) s++; if(!strncmp(s, "-t", 2)) { if((tno = getfloat(s+2, st->datatime)) < 0) { msg(ausage); continue; } for(s += 2; isspace(*s) || *s=='+' || *s=='-'; s++) ; while(isdigit(*s)) s++; while(*s && isspace(*s)) s++; } if(*s == '"') s++; specks_add_annotation( st, s, tno ); } else if(!strcmp(key, "size")) { key = strtok(NULL, separ); if(key) s.size = atof(key); } else if(!strcmp(key, "scale")) { /* "scale" in data file, not as VIRDIR command */ float v; key = strtok(NULL, separ); if(key != NULL && (v = atof(key)) != 0) st->spacescale = speckscale = v; } else if(!strcmp(key, "tfm")) { int inv = 0; int mul = 0; int hpr = 0; int any, more; float scl; Matrix ot, t; Point xyz; float aer[3]; key = strtok(NULL, "\n"); if(key == NULL) key = ""; for(more = 1; *key != '\0' && more; key++) { switch(*key) { case '*': mul = 1; break; case '/': inv = 1; break; case 'h': case 'p': case 'r': hpr = 1; break; case ' ': case '\t': break; default: more = 0; key--; break; } } parti_geto2w( st, parti_object( NULL, &st ), &ot ); switch(any = sscanf(key, "%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f", &t.m[0],&t.m[1],&t.m[2],&t.m[3], &t.m[4],&t.m[5],&t.m[6],&t.m[7], &t.m[8],&t.m[9],&t.m[10],&t.m[11], &t.m[12],&t.m[13],&t.m[14],&t.m[15])) { case 1: scl = t.m[0]; t = Tidentity; t.m[0*4+0] = t.m[1*4+1] = t.m[2*4+2] = scl; break; case 6: case 7: /* ignore fovy if included */ xyz = *(Point *)&t.m[0]; if(hpr) { aer[0] = t.m[3], aer[1] = t.m[4], aer[2] = t.m[5]; } else { /* Note we permute: px py pz rx ry rz == px py pz e a r */ aer[1] = t.m[3], aer[0] = t.m[4], aer[2] = t.m[5]; } xyzaer2tfm( &t, &xyz, aer ); break; case 16: break; case 0: t = ot; break; default: msg("Usage: tfm: expected 16 numbers"); continue; } if(any) { if(inv) eucinv( &t, &t ); if(mul) mmmul( &t, &ot, &t ); if(strchr(key, '*')) mmmul( &t, &t, &ot ); /* with trailing '=', just print result without assignment */ if(NULL==strchr(key, '=')) parti_seto2w( st, parti_object( NULL, &st ), &t ); } tfm2xyzaer( &xyz, aer, &t ); msg("obj2w: x y z rx ry rz %g %g %g %g %g %g", xyz.x[0],xyz.x[1],xyz.x[2], aer[1],aer[0],aer[2] ); for(i = 0; i < 3; i++) msg(" %10.7f %10.7f %10.7f %10.7f", t.m[i*4+0],t.m[i*4+1],t.m[i*4+2],t.m[i*4+3]); msg(" %10.7g %10.7g %10.7g %10.7g", t.m[3*4+0],t.m[3*4+1],t.m[3*4+2],t.m[3*4+3]); eucinv( &t, &t ); tfm2xyzaer( &xyz, aer, &t ); msg("w2obj: x y z rx ry rz %g %g %g %g %g %g", xyz.x[0],xyz.x[1],xyz.x[2], aer[1],aer[0],aer[2] ); for(i = 0; i < 3; i++) msg(" %10.7f %10.7f %10.7f %10.7f", t.m[i*4+0],t.m[i*4+1],t.m[i*4+2],t.m[i*4+3]); msg(" %10.7g %10.7g %10.7g %10.7g", t.m[3*4+0],t.m[3*4+1],t.m[3*4+2],t.m[3*4+3]); } else if(!strcmp(key, "eval") || !strcmp(key, "feed") || !strcasecmp(key, "VIRDIR")) { char *av[128]; int ac; char *cmd = strtok(NULL, "\n"); char *savedcmd = NewA( char, strlen(cmd) + 1 ); strcpy(savedcmd, cmd); key = strtok(cmd, separ); for(ac = 0; ac < 127 && key != NULL; ac++) { av[ac] = key; key = strtok(NULL, separ); } av[ac] = NULL; #if CAVE if(0 == specks_parse_args( &st, ac, av )) VIDI_queue_commandstr( savedcmd ); #else /* non-CAVE */ specks_parse_args( &st, ac, av ); #endif /* non-CAVE */ } else if(!strcmp(key, "ignorefirst") || !strcmp(key, "ignorepgc")) { ignorefirst = 1; } else if(!strcmp(key, "filepath")) { int m, k = 0; char **oldpath = getfiledirs(); char *path[64]; while(k < 64-1 && (key = strtok(NULL, ": \t\r\n")) != NULL) { if(!strcmp(key, "+")) { for(m = 0; k<64-1&&oldpath!=NULL&&oldpath[m]!=NULL; m++) path[k++] = strdup(oldpath[m]); } else { path[k++] = strdup(key); } } path[k] = NULL; if(k > 0) filedirs(path); while(--k >= 0) free(path[k]); } else if(!strcmp(key, "texture")) { int txno = 0; int txflags = TXF_SCLAMP | TXF_TCLAMP; int txapply = TXF_DECAL; int qual = 7; char *txfname; key = strtok(NULL, separ); sscanf(key, "%d", &txno); while(key != NULL && (key[0] == '-' || key[0] == '+')) { int tqual = (strchr(key,'m') ? 4:0) | (strchr(key,'l') ? 2:0) | (strchr(key,'n') ? 1:0); if(key[0] == '-') qual &= ~tqual; else qual |= tqual; if(strchr(key,'a')) txflags |= TXF_ALPHA; if(strchr(key,'i')) txflags |= TXF_INTENSITY; if(strchr(key, 'A')) txflags |= TXF_ADD; if(strchr(key, 'M')) txapply = TXF_MODULATE; if(strchr(key, 'D')) txapply = TXF_DECAL; if(strchr(key, 'B')) txapply = TXF_BLEND; key = strtok(NULL, separ); } if(key == NULL || (txno == 0 && sscanf(key, "%d", &txno) <= 0) || (txfname = strtok(NULL, separ)) == NULL) { msg("Expected ``texture [-lmnaMDB] txno file.sgi'', got %s", oline); msg(" opts: -l(inear) -m(ipmap) -n(earest) -i(intensity) -a(lpha) -A(dd) -M(odulate)|-D(ecal)|-B(lend))"); } else { txaddentry( &st->textures, &st->ntextures, fname, txno, txfname, txapply, txflags, qual ); } } else if(!strcmp(key, "polyorivar")) { if((key = strtok(NULL, separ)) != NULL) sscanf(key, "%d", &st->polyorivar0); } else if(!strcmp(key, "texturevar")) { if((key = strtok(NULL, separ)) != NULL) sscanf(key, "%d", &st->texturevar); } else if(!strncmp(key, "coord", 5) || !strncmp(key, "altcoord", 8)) { float *t = &st->altcoord[0].w2coord.m[0]; key = strtok(NULL, separ); if(key == NULL) { msg("coord %s %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", st->altcoord[0].name, t[0],t[1],t[2],t[3], t[4],t[5],t[6],t[7], t[8],t[9],t[10],t[11], t[12],t[13],t[14],t[15]); } else { sprintf(st->altcoord[0].name, "%.9s", key); key = strtok(NULL, "\n"); if(key != NULL && 16 != sscanf(key, "%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f%f", t,t+1,t+2,t+3, t+4,t+5,t+6,t+7, t+8,t+9,t+10,t+11, t+12,t+13,t+14,t+15)) { key = NULL; } if(key == NULL) { msg("expected \"coord\" name ... 16 world-to-coord tfm floats (GL order) ..."); } } } else if(!strncmp(key, "dataset", 7)) { char name[12]; int indexno; if((key = strtok(NULL,"\n")) == NULL || sscanf(key, "%d %11s", &indexno, name) < 2 || indexno < 0 || indexno >= MAXFILES) { msg("%s: expected ``dataset <indexno> <datasetname>'' with 0<=indexno<=%d", fname, MAXFILES-1); continue; } strncpyt(st->dataname[indexno], name, sizeof(st->dataname[indexno])); st->curdata = indexno; } else if(!strncmp(key, "datavar", 7)) { int varno, nargs; struct valdesc vd; if((key = strtok(NULL, "\n")) == NULL || (nargs = sscanf(key, "%d %19s %f %f", &varno, vd.name, &vd.min, &vd.max)) < 2 || varno < 0 || varno >= MAXVAL) { msg("%s: expected ``datavar <indexno> <variablename>'' with 0<=indexno<=%d", fname, MAXVAL-1); continue; } strcpy(st->vdesc[st->curdata][varno].name, vd.name); if(nargs == 4) { st->vdesc[st->curdata][varno].min = vd.min; st->vdesc[st->curdata][varno].max = vd.max; } } else if(!strcmp(key, "datatime")) { int newt = st->curtime; /* so e.g. "datatime now" => apply to current data */ if((key = strtok(NULL, separ)) == NULL) { msg("%s: datatime %d", fname, st->datatime); continue; } sscanf(key, "%d", &newt); if(newt < 0) { msg("%s: datatime %s: timestep must be >= 0", fname, key); continue; } if(newt == st->datatime) /* no need to change anything */ continue; if(nsp > 0) { /* else flush accumulated specks */ addchunk( st, nsp, sizeof(struct speck), speckscale, sp, NULL, maxfields > MAXVAL ? sizeof(struct speck) : SMALLSPECKSIZE( maxfields ) ); nsp = maxfields = 0; } st->datatime = newt; } else if(!strcmp(key, "ellipsoid")) { } else if(!strcmp(key, "textcolor")) { key = strtok(NULL, separ); if(key) sscanf(key, "%d", &s.rgba); } else { struct valdesc *vdp; i = 0; if(!ignorefirst) s.p.x[i++] = atof(key) * speckscale; for( ; i < 3 && (key = strtok(NULL,separ)) != NULL; i++) { s.p.x[i] = atof(key) * speckscale; } if(i < 3) continue; vdp = &st->vdesc[st->curdata][0]; for(i = 0; (key = strtok(NULL,separ)) != NULL; i++) { if(key[0] == 't') break; /* "text" */ if(key[0] == '#') break; if(i < COUNT(s.val)) { s.val[i] = atof(key); if(vdp->nsamples++ == 0) { vdp->min = vdp->max = s.val[i]; } else { if(vdp->min > s.val[i]) vdp->min = s.val[i]; else if(vdp->max < s.val[i]) vdp->max = s.val[i]; } vdp->sum += s.val[i]; vdp->nsamples++; vdp->mean = vdp->sum / vdp->nsamples; vdp++; } } if(maxfields < i) maxfields = i; s.title[0] = '\0'; if(key && key[0] == '#') { key = strtok(NULL, "\r\n"); if(key) { while(isspace(key[0])) key++; for(i = strlen(key); --i >= 0 && isspace(key[i]); ) key[i] = '\0'; sprintf(s.title, "%.*s", (int)(sizeof(s.title)-1), key); maxfields = MAXVAL+1; /* "keep title too" */ } } sp[nsp++] = s; if(key != NULL && key[0] == 't') { /* "text" */ s.size = 1; key = strtok(NULL, "\n"); if(key && !strncmp(key, "-size", 5)) { s.size = strtod( key+5, &key ); if(*key) key++; if(s.size == 0) s.size = 1; } addchunk( st, 1, SMALLSPECKSIZE(0), speckscale, &s, key, SMALLSPECKSIZE(0) ); nsp--; } } } fclose(f); SPFLUSH(); *stp = st; } /* Add a static (eternal) box to display list */ int specks_add_box( struct stuff *st, struct AMRbox *box, int timestep ) { int i; if(box->level >= 0 && st->boxlevels <= box->level) st->boxlevels = box->level+1; for(i = 0; i < st->staticboxroom && st->staticboxes[i].level >= 0; i++) { if(box->boxno != -1 && box->boxno == st->staticboxes[i].boxno) { st->staticboxes[i] = *box; return ~i; } } if(i >= st->staticboxroom-1) { st->staticboxroom = (st->staticboxroom>0) ? st->staticboxroom*2 : 12; st->staticboxes = RenewN( st->staticboxes, struct AMRbox, st->staticboxroom ); } st->staticboxes[i] = *box; st->staticboxes[i+1].level = -1; return i; } void specks_read_boxes( struct stuff *st, char *fname, int timebase ) { FILE *f = fopen(fname, "r"); char line[1024]; int ntimes = -1, maxbox = -1, curbox = 0, curtime = 0, level = -1, i; int lno = 0; int boxseq, boxno; struct AMRbox *boxes = NULL; struct AMRbox box; if(timebase < 0) /* if unspecified time, start at first timestep */ timebase = 0; if(f == NULL) { msg("Can't open AMRboxes file %s (as from hier2boxes.pl)", fname); return; } while(fgets(line, sizeof(line), f) != NULL) { lno++; boxno = -1; if(sscanf(line, "%f%f%f %f%f%f # %d", &box.p0.x[0],&box.p0.x[1],&box.p0.x[2], &box.p1.x[0],&box.p1.x[1],&box.p1.x[2], &boxno) >= 6) { for(i = 0; i < 3; i++) { box.p0.x[i] *= st->spacescale; box.p1.x[i] *= st->spacescale; } if(boxno != -1) box.boxno = boxno; else box.boxno = boxseq++; if(curbox < maxbox && boxes != NULL && level >= 0) { boxes[curbox] = box; curbox++; } else { msg("%s line %d: Excess box (only expected %d at timestep %d)", fname, lno, maxbox, curtime); } } else if(sscanf(line, "AMRboxes %d timesteps", &ntimes) > 0 && ntimes >= 0) { int needtimes = ntimes + (timebase<0 ? 0 : timebase); if(st->boxtimes <= needtimes+1) { needtimes += st->boxtimes + 15; /* leave lots of extra room */ st->boxes = RenewN( st->boxes, struct AMRbox *, needtimes ); memset( &st->boxes[st->boxtimes], 0, (needtimes - st->boxtimes) * sizeof(struct AMRbox *) ); if(st->boxtimes < ntimes+timebase) st->boxtimes = ntimes+timebase; } boxes = NULL; } else if(sscanf(line, "timestep %d %d grids", &curtime, &maxbox) == 2) { boxseq = 1; if(ntimes < 0) { msg( "%s line %d: ``AMRboxes N timesteps'' must precede ``timestep'' lines!", fname, lno); curtime = -1; boxes = NULL; continue; } else if(curtime<0 || curtime>=ntimes || maxbox<0) { msg("%s line %d: bad timestep number", fname, lno); curtime = -1; boxes = NULL; continue; } if((boxes = st->boxes[curtime+timebase]) != NULL) { msg("%s line %d: Respecifying timestep %d", fname, lno, curtime); Free( st->boxes[curtime+timebase] ); } boxes = NewN( struct AMRbox, maxbox+1 ); for(curbox = 0; curbox <= maxbox; curbox++) boxes[curbox].level = -(maxbox+1); st->boxes[curtime+timebase] = boxes; /* Ensure that known span of time includes this timestep */ (void) specks_timespecksptr( st, st->curdata, curtime+timebase ); curbox = 0; } else if(sscanf(line, "level %d", &level) > 0) { if(boxes == NULL) { msg("%s line %d: Must specify timestep before level", fname, lno); level = -1; } if(st->boxlevels <= level) st->boxlevels = level+1; box.level = level; } } fclose(f); /* st->boxlevelmask |= (1 << st->boxlevels) - 1; * Don't do this -- it turns on all boxlevels whenever we get a new box! */ } static struct AMRbox *findboxno( struct AMRbox *box, int boxno, int *count, int *bmin, int *bmax ) { if(box == NULL) return NULL; while(box->level >= 0) { if(box->boxno == boxno) return box; if(*bmin > box->boxno) *bmin = box->boxno; if(*bmax < box->boxno) *bmax = box->boxno; ++*count; box++; } return NULL; } int specks_gobox( struct stuff *st, int boxno, int argc, char *argv[] ) { Point pmid; float sz, scale; char cmd[128]; int bmin = 1<<30, bmax = -1<<30; int count = 0; struct AMRbox *box = NULL; if(st->boxes && st->curtime < st->boxtimes) box = findboxno( st->boxes[st->curtime], boxno, &count, &bmin, &bmax ); if(box == NULL) box = findboxno( st->staticboxes, boxno, &count, &bmin, &bmax ); if(box == NULL) { if(count == 0) msg("No AMR boxes for timestep %d", st->curtime); else msg("%d AMR boxes (numbered %d..%d) for timestep %d, but no box number %d", count, bmin, bmax, st->curtime, boxno); return 0; } vlerp( &pmid, .5*st->gscale, &box->p0, &box->p1 ); set_interest_point( &pmid ); sz = vdist( &box->p0, &box->p1 ); sprintf(cmd, "%.1g", st->gscale * st->goboxscale * sz); /* round to 1 decimal */ scale = atof(cmd); #if CAVE { Point pmid, fwd, headv, pos, offset, newpos; static Point zvec = {0,0,1}; CAVENavConvertVectorCAVEToWorld( zvec.x, fwd.x ); CAVEGetPosition( CAVE_HEAD, headv.x ); headv.x[2] -= 10.0; /* a point in front of head, in CAVE coords */ CAVENavConvertVectorCAVEToWorld( headv.x, offset.x ); msg("scale %g caveunit %g offsetW %g %g %g", scale, vlength(&fwd), offset.x[0],offset.x[1],offset.x[2]); vsadd( &newpos, &pmid, -scale / vlength(&fwd), &offset ); sprintf(cmd, sz > 0 ? "jumpto %g %g %g . . . %g" : "jumpto %g %g %g", newpos.x[0], newpos.x[1], newpos.x[2], scale); VIDI_queue_commandstr( cmd ); } #endif return 1; } void specks_read_cmap( struct stuff *st, char *fname, int *ncmapp, struct cment **cmapp ) { int i; char *cp; char line[256]; int k, count = 0; int *tcmap; struct cment *cm; float fr,fg,fb,fa; int big = 0; int lno = 0; int ncmap = 0; int cval; int csrc; FILE *f = fopen(fname, "r"); if(f == NULL) { msg("Can't open colormap file %s", fname); return; } tcmap = NULL; ncmap = 0; while(fgets(line, sizeof(line), f) != NULL) { lno++; cp = line; rescan: for( ; isspace(*cp); cp++) ; if(*cp == '\0' || *cp == '#') continue; fa = 1.0; k = sscanf(cp, "%f%f%f", &fr,&fg,&fb/*,&fa*/); if(k == 1) { if(count == 0 && fr == (int)fr && fr > 0) { count = fr; if(count >= 1 && count < 1000000) /* OK */ continue; msg("Unreasonable number of colormap entries claimed in header"); /* and fall into error case */ } else { while(isspace(*cp) || isdigit(*cp)) cp++; if(*cp == ':') { ncmap = fr; if(ncmap < 0) { ncmap = 0; /* don't continue -- fall into error */ } else if(ncmap >= count) { msg("Colormap index %g exceeds size %d given in header", fr, count); /* don't continue -- ditto */ } else { cp++; goto rescan; /* All's well */ } } } } else if(k >= 3) { if(fr > 2 || fg > 2 || fb > 2 || fa > 2) big = 1; if(!big) { fr *= 255; fg *= 255; fb *= 255; /*fa *= 255;*/ } cval = PACKRGBA( (int)fr, (int)fg, (int)fb, 0 ); if(tcmap == NULL) { tcmap = NewA( int, count ); /* Fill unused entries with gray */ memset(tcmap, 0x80, count*sizeof(int)); } tcmap[ncmap>=count ? count-1 : ncmap<0 ? 0 : ncmap] = cval; ncmap++; if(ncmap >= count) break; continue; } else if(sscanf(cp, "=%d", &csrc) > 0) { if(csrc < 0 || csrc >= count || ncmap < 0 || ncmap >= count || tcmap == NULL) { msg("Colormap index out of range: %d or %d isn't in 0..%d", ncmap, csrc, count-1); } else { tcmap[ncmap++] = tcmap[csrc]; continue; } /* Fall through into error */ } msg("Trouble reading colormap file %s: bad line %d: %s", fname, lno, line); fclose(f); return; } fclose(f); if(tcmap == NULL) return; *ncmapp = count; if(*ncmapp < 2) { *ncmapp = 2; tcmap[1] = tcmap[0]; } *cmapp = cm = RenewN( *cmapp, struct cment, *ncmapp ); k = *ncmapp; for(i = 0; i < k; i++) { cm[i].raw = tcmap[i]; cm[i].cooked = specks_cookcment( st, tcmap[i] ); } } #if CAVEMENU void set_psize( float psize, MenuEnt *me, void *st ) { char str[20]; ((struct stuff *)st)->psize = psize; sprintf(str, "pointsize %.3g", psize); menu_settitle( me, str ); } /* set_tknob() only used if PARTIMENU mentions "survey" */ void set_tknob( float val, MenuEnt *me, void *vtb ) { char str[128]; struct stuff *st = ((struct boxleveler *)vtb)->st; int self = ((struct boxleveler *)vtb)->level; static int mod[3] = { 1, 4, 16 }; static char *fmt[3] = { "StarMassEjFrac [%d/4]", "SNFeedback [%d/4]", "StarEfficiency [%d/4]", }; int range = 4, mine, rest, above; above = mod[self] * range; rest = st->curtime % mod[self] + (st->curtime / above) * above; mine = (st->curtime / mod[self]) % range; if(me->state == ME_PICKED || me->state == ME_HELD) { mine = val; if(mine < 0) mine = 0; else if(mine >= range) mine = range-1; clock_set_time( st->clk, rest + mine*mod[self] ); clock_set_running( st->clk, 0 ); } sprintf(str, fmt[self], mine+1); menu_settitle( me, str ); } void set_step( float time0, MenuEnt *me, void *vst ) { char str[32]; struct stuff *st = (struct stuff *)vst; if(me->state == ME_PICKED) { time0 += clock_fwd(st->clk); st->timeplay = 0; me->state = ME_HELD; } clock_set_time( st->clk, me->val = (int) time0 ); /* NOTE we use the static values snapped by the framefunction. * st->curtime etc. might have changed since then. */ sprintf(str, "step %2d of 0..%d", st->frame_time, st->ntimes - 1); if(st->fetching > 0) sprintf(str+strlen(str), " (loading %d)", st->fetchtime); menu_settitle( me, str ); } void set_fwd( int fwd, MenuEnt *me, void *st ) { clock_set_fwd( ((struct stuff *)st)->clk, fwd ); menu_settitle( me, fwd ? ">>> fwd >>>" : "<<< rev <<<" ); } void set_viewall( int all, MenuEnt *me, void *vst ) { struct stuff *st = (struct stuff *)vst; if(all) { strcpy( st->vdcmd, "\002jump all" ); menu_settitle( me, "view all" ); } else { strcpy( st->vdcmd, "\002jump peak" ); menu_settitle( me, "view peak" ); } } float get_nav_scale(void) { float nav[4][4]; CAVENavGetMatrix( nav ); return 1 / sqrtf( vdot( (Point *)&nav[0][0], (Point *)&nav[0][0] ) ); } void set_scale( float logscale, MenuEnt *me, void *vst ) { struct stuff *st = (struct stuff *)vst; float scl = pow(10., logscale); char str[64]; if(me->state == ME_PICKED || me->state == ME_HELD) { sprintf( st->vdcmd, "\002scale %g", scl); } else { scl = get_nav_scale(); me->val = scl<=0 ? -6 : log10( scl ); } sprintf( str, "scale %.4g", scl ); menu_settitle( me, str ); } void set_dataset( int which, MenuEnt *me, void *vst ) { char str[32]; struct stuff *st = (struct stuff *)vst; if(which < 0 || which >= st->ndata || which >= MAXFILES) which = 0; st->curdata = which; sprintf(str, "viewing %s (%d)", st->dataname[which], which); menu_settitle( me, str ); } void set_lumvar( int lumvar, MenuEnt *me, void *vst ) { char str[80]; struct stuff *st = (struct stuff *)vst; struct valdesc *vd; if(lumvar < 0 || lumvar >= MAXVAL+1) lumvar = 0; st->sizedby = lumvar; vd = &st->vdesc[st->curdata][lumvar]; if(vd->lmin == vd->lmax) vd->lmin = vd->min, vd->lmax = vd->max; sprintf(str, "lum by %s (%d) [%.3g..%.3g]", vd->name, lumvar, vd->lmin, vd->lmax); menu_settitle( me, str ); } void set_colorvar( int colorvar, MenuEnt *me, void *vst ) { char str[80]; struct stuff *st = (struct stuff *)vst; struct valdesc *vd; if(colorvar < 0 || colorvar >= MAXVAL+1) colorvar = 0; st->coloredby = colorvar; vd = &st->vdesc[st->curdata][colorvar]; if(vd->cmin == vd->cmax) vd->cmin = vd->min, vd->cmax = vd->max; sprintf(str, "color by %s (%d) [%.3g..%.3g]", vd->name, colorvar, vd->cmin, vd->cmax); menu_settitle( me, str ); } void set_slum( float slum, MenuEnt *me, void *vst ) { char str[32]; struct stuff *st = (struct stuff *)vst; st->vdesc[st->curdata][st->sizedby].lum = slum; sprintf(str, slum == 0 ? "scale-lum <unset>" : "scale-lum %.2g", slum); menu_settitle( me, str ); } void set_speed( float speed, MenuEnt *me, void *vst ) { char str[32]; struct stuff *st = (struct stuff *)vst; specks_set_speed( st, speed ); sprintf(str, "speed %.1f steps/anim sec", speed); menu_settitle( me, str ); } void set_fspeed( float speed, MenuEnt *me, void *vst ) { char str[64]; struct stuff *st = (struct stuff *)vst; specks_set_fspeed( st, speed ); if(me->state == ME_PICKED) { clock_set_running( st->clk, !clock_running(st->clk) ); me->state = ME_HELD; /* only once */ } sprintf(str, "fspeed %.1f steps/real sec (%s)", speed, clock_running(st->clk) ? "running" : "stopped"); menu_settitle( me, str ); } void set_seedata( int which, MenuEnt *me, void *vst ) { struct stuff *st = (struct stuff *)vst; char str[64]; if(which >= st->ndata) which = st->ndata - 1; if(which < 0) which = 0; st->curdata = which; sprintf(str, "see %.24s (%d)", st->dataname[which], which); menu_settitle( me, str ); } void set_every( float every, MenuEnt *me, void *vst ) { char str[32]; struct stuff *st = (struct stuff *)vst; if(every < 1) every = 1; st->subsample = me->val = every; sprintf(str, "every %dth p'cle (of %d)", (int)every, specks_count(st->sl)); menu_settitle( me, str ); } void set_menu( int on, MenuEnt *me, void *vst ) { struct stuff *st = (struct stuff *)vst; int empty = st->ntimes == 0 && st->sl == NULL; if(me->state == ME_PICKED) st->hidemenu = !st->hidemenu; menu_sethidden( pmenu, st->hidemenu || empty ); menu_sethidden( stubmenu, st->hidemenu<=0 || empty ); } void set_allboxes( int on, MenuEnt *me, void *vst ) { struct stuff *st = (struct stuff *)vst; st->useboxes = on; menu_settitle( me, on==2 ? "Boxes ONLY" : on ? "Boxes ON" : "Boxes off" ); } void set_boxlevel( int on, MenuEnt *me, void *vblr ) { struct boxleveler *blr = (struct boxleveler *)vblr; struct stuff *st = blr->st; int bit = 1 << blr->level; char str[32]; if(blr->level >= st->boxlevels) { menu_settitle(me, "" ); } else { if(me->state == ME_PICKED) st->boxlevelmask ^= bit; sprintf(str, "Level %d %s", blr->level, st->boxlevelmask & bit ? "ON" : "off" ); menu_settitle( me, str ); } } void set_point( int on, MenuEnt *me, void *st ) { struct stuff *stuff = (struct stuff *)st; stuff->usepoint = (on<0) ? !stuff->usepoint : on; menu_settitle( me, stuff->usepoint ? "Point On" : "Point Off" ); } void set_poly( int on, MenuEnt *me, void *st ) { struct stuff *stuff = (struct stuff *)st; stuff->usepoly = (on<0) ? !stuff->usepoly : on; menu_settitle( me, stuff->usepoly ? "Poly On" : "Poly Off" ); } void set_label( int on, MenuEnt *me, void *st ) { struct stuff *stuff = (struct stuff *)st; stuff->usetext = (on<0) ? !stuff->usetext : on; menu_settitle( me, stuff->usetext ? "Labels On" : "Labels Off" ); } void set_alpha( float alpha, MenuEnt *me, void *st ) { char str[20]; ((struct stuff *)st)->alpha = alpha; sprintf(str, "Alpha %.2f", alpha); menu_settitle( me, str ); } void set_fog( float fog, MenuEnt *me, void *st ) { char str[20]; ((struct stuff *)st)->fog = fog; sprintf(str, "Fog %.2f (not yet)", fog); menu_settitle( me, str ); } void set_polysize( float polysize, MenuEnt *me, void *st ) { char str[20]; ((struct stuff *)st)->polysize = polysize; sprintf(str, "Spotsize %.2f", polysize); menu_settitle( me, str ); } #endif /*CAVEMENU*/ int specks_set_byvariable( struct stuff *st, char *str, int *val ) { int i; char *ep; int best = -1; if(str == NULL) return 0; if(!strcasecmp( str, "const" ) || !strcasecmp( str, "constant" ) || !strcasecmp( str, "rgb" )) { *val = CONSTVAL; return -1; } for(i = 0; i < MAXVAL; i++) { if(strncasecmp( str, st->vdesc[st->curdata][i].name, strlen(str) ) == 0) { best = i; if(!strcmp( str, st->vdesc[st->curdata][i].name )) break; } } if(best >= 0) { *val = best; return 1; } i = strtol(str, &ep, 0); if(ep == str || i < 0 || i > MAXVAL || (*ep != '\0' && *ep != '(')) return 0; *val = i; return 1; } static char *putcoords( char *buf, Point *pos, Matrix *T, int isvec, char *cartfmt, char *lonlatfmt, char *hmdmfmt ) { Point p; char *cp = buf; float lat, lon, r; if(T == NULL) T = &Tidentity; buf[0] = '\0'; if(isvec) vtfmvector( &p, pos, T ); else vtfmpoint( &p, pos, T ); if(cartfmt) cp += sprintf(cp, cartfmt, p.x[0],p.x[1],p.x[2]); lon = atan2(p.x[1], p.x[0]) * 180/M_PI; if(lon < 0) lon += 360; lat = atan2(p.x[2], hypot(p.x[1],p.x[0])) * 180/M_PI; r = vlength(&p); if(lonlatfmt) cp += sprintf(cp, lonlatfmt, lon, lat, r); if(hmdmfmt) { char *sign = (lat < 0) ? "-" : "+"; lat = fabs(lat); lon /= 15; cp += sprintf(cp, hmdmfmt, (int)lon, 60. * (lon - (int)lon), sign, (int)lat, 60. * (lat - (int)lat), r); } return buf; } static char *whereis(struct stuff *st, char *buf, Point *pos, int isvec) { putcoords(buf, pos, NULL, isvec, "%.2f %.2f %.2f", NULL, NULL); if(strstr(st->altcoord[0].name, "2000") || strstr(st->altcoord[0].name, "1950") || strstr(st->altcoord[0].name, "eq")) { sprintf(buf + strlen(buf), "; %.9s: ", st->altcoord[0].name); putcoords(buf+strlen(buf), pos, &st->altcoord[0].w2coord, isvec, NULL, NULL, "%02d:%02.0f %s%02d:%02.0f %g"); } else if(st->altcoord[0].name[0] != '\0') { sprintf(buf + strlen(buf), "; %.9s: ", st->altcoord[0].name); putcoords(buf+strlen(buf), pos, &st->altcoord[0].w2coord, isvec, NULL, "%.2f %.2f %g", NULL); } return buf; } static Point zero = {0,0,0}, forward = {0,0,-1}; #if CAVE void set_where(int which, MenuEnt *me, void *vst) { struct stuff *st = (struct stuff *)vst; char buf[180]; Point pos; Matrix cam2w; switch(which) { case 0: CAVEGetPosition( CAVE_HEAD_NAV, pos.x ); sprintf(buf, "head at "); whereis(st, buf+strlen(buf), &pos, 0); break; case 1: VD_get_cam2world_matrix( cam2w.m ); vtfmpoint( &pos, &zero, &cam2w ); sprintf(buf, "camera at "); whereis(st, buf+strlen(buf), &pos, 0); break; case 2: VD_get_cam2world_matrix( cam2w.m ); vtfmvector( &pos, &forward, &cam2w ); sprintf(buf, "cam looks "); whereis(st, buf+strlen(buf), &pos, 1); break; case 3: VD_get_interest( pos.x ); sprintf(buf, "interest at "); whereis(st, buf+strlen(buf), &pos, 0); break; default: return; } menu_settitle( me, buf ); } #endif /*CAVE*/ static void tellwhere(struct stuff *st) { Point pos, fwd; Matrix cam2w; char buf[180]; #ifdef CAVE CAVENavConvertCAVEToWorld( zero.x, pos.x ); msg("cave at %s", whereis(st, buf, &pos, 0)); CAVEGetPosition( CAVE_WAND_NAV, pos.x ); msg("wand at %s", whereis(st, buf, &pos, 0)); CAVEGetVector( CAVE_WAND_FRONT_NAV, fwd.x ); VD_get_cam2world_matrix( cam2w.m ); #else parti_getc2w( &cam2w ); #endif vtfmpoint( &pos, &zero, &cam2w ); vtfmvector( &fwd, &forward, &cam2w ); msg("camera at %s", whereis(st, buf, &pos, 0)); msg("looking to %s", whereis(st, buf, &fwd, 1)); } FILE *asyncmd[MAX_ASYNC]; int specks_add_async( struct stuff *st, char *cmdstr ) { int i; #if unix /* not WIN32 */ if(cmdstr == NULL) return -1; for(i = 0; i < MAX_ASYNC; i++) { if(asyncmd[i] == NULL) { asyncmd[i] = popen(cmdstr, "r"); #if !CAVEMENU parti_asyncfd( fileno( asyncmd[i] ) ); #endif return i; } } #endif /*unix*/ msg("Sorry, all %d async-command slots full: can't run %s", MAX_ASYNC, cmdstr); return -1; } int specks_check_async( struct stuff **stp ) { int i, any = 0; char *av[128]; int ac; char buf[10240], vdbuf[1024]; static int reentered = 0; /* Don't allow recursion! */ #if unix /* not WIN32 */ if(stp == NULL || *stp == NULL) return 0; if(reentered>0) return 0; reentered = 1; for(i = 0; i < MAX_ASYNC; i++) { int brackets = 0; int none = 1; int nlines = 0; while(asyncmd[i] && (none || brackets > 0)) { int c = (brackets>0) ? fnextc(asyncmd[i], 1) : async_fnextc(asyncmd[i], 1); switch(c) { case EOF: if(getenv("DBG")) msg("Closing async %d", i); #if !CAVEMENU parti_unasyncfd( fileno( asyncmd[i] ) ); #endif pclose(asyncmd[i]); asyncmd[i] = NULL; break; case -2: /* NODATA */ none = 0; break; default: /* Got something. Assume that we can read a whole line quickly. */ if(fgets(buf, sizeof(buf), asyncmd[i]) == NULL) { if(getenv("DBG")) msg("fgets: Closing async %d", i); pclose(asyncmd[i]); asyncmd[i] = NULL; break; } nlines++; any++; sprintf(vdbuf, "%.1023s", buf); for(c = 0; isspace(buf[c]) || buf[c] == '{' || buf[c] == '}'; c++) { if(buf[c] == '{') brackets++; else if(buf[c] == '}' && brackets > 0) brackets--; buf[c] = ' '; } if(getenv("DBG")) msg("async %d[%d]{%d}: %s", i, nlines, brackets, buf); av[0] = strtok(buf, separ); for(ac = 1; ac < 127 && (av[ac] = strtok(NULL, separ)) != NULL; ac++) ; av[ac] = NULL; if(av[0] != NULL) { none = 0; #if CAVEMENU if(!specks_parse_args( stp, ac, av )) VIDI_queue_commandstr( vdbuf ); #else /* non-virdir version */ parti_redraw(); specks_parse_args( stp, ac, av ); #endif } } } } reentered = 0; #endif /*unix not WIN32*/ return any; } int getbool( char *str, int defval ) { int v; char *ep; if(str == NULL) return defval; if(!strcasecmp(str, "on")) return 1; if(!strcasecmp(str, "off")) return 0; if(!strcasecmp(str, "toggle")) return !defval; if(!strcasecmp(str, "all")) return -1; v = strtol(str, &ep, 0); if(str == ep) return defval; return v; } double getfloat( char *str, double defval ) { double v; char *ep; int prefix = 0; if(str == NULL) return defval; if(str[0] == '-' && (str[1] == '=' || str[1] == '-')) /* -=, -- */ prefix = *str++; if(str[0] == '*' || str[0] == '+' || str[0] == '/' || str[0] == 'x') prefix = *str++; if(str[0] == '=') str++; v = strtod(str, &ep); if(ep == str) { v = defval; } else { switch(prefix) { case '-': v = defval - v; break; case '+': v = defval + v; break; case '*': v = defval * v; break; case '/': v = (v != 0) ? defval / v : 0; break; } /* default: just use v */ } return v; } int specks_parse_args( struct stuff **stp, int argc, char *argv[] ) { int i; struct stuff *st = *stp; while( argc>0 && (!strncmp( argv[0], "specks", 4 ) || !strcmp( argv[0], "feed" ) )) { argc--, argv++; /* VD_select_menu( specks_menuindex ); */ } if(argc <= 0) return 0; if(!strcmp( argv[0], "?" ) || !strcmp( argv[0], "help" )) { static char *help1[] = { "specks commands:", " speed data-steps per VirDir second", " step N -or- step +N -or- step -N Go to data step N, or step fwd/back", " trange on|off|MIN MAX [WRAP] limit range of datastep times", " run toggle auto-play (run/stop)", " color VARNO-or-NAME color particles by VARNO'th variable (0..%d)", " color const R G B set all particles to be that color", " lum VARNO-or-NAME tie particle size/luminosity to VARNOth var", " lum const LUM set all particles to be that brightness", " slum SCALEFACTOR scale particle size by SCALEFACTOR", " psize SIZE scale particle size by SIZE * SCALEFACTOR", " depthsort sort polygons by depth", " see DATASETNO-or-NAME show that dataset (e.g. \"see 0\" or \"see gas\")", " read [-t time] DATAFILENAME read data file (e.g. to add new specks)", " ieee [-t time] IEEEIOFILE read IEEEIO file (starting at given timestep)", " sdb [-t time] SDBFILE read .sdb star-data file", " annot [-t time] string set annotation string (for given timestep)", " add DATAFILECOMMAND enter a single datafile command (ditto)", " every N subsample: show every Nth particle", " bound show bounds (coordinate range of all particles)", " clipbox {on | off | X0,X1 Y0,Y1 Z0,Z1 | CENX,Y,Z RADX,Y,Z | X0 Y0 Z0 X1 Y1 Z1} clipping region", " add box [-n boxno] [-l level] CENX,Y,Z RX,RY,RZ | X0 Y0 Z0 X1 Y1 Z1 marker-box", " boxlabels show box numbers", " boxaxes show R/G/B axes from X0,Y0,Z0 box corner" #if CAVEMENU " boxes {off|on|only} hide/show all AMR boxes", " {hide|show} LEVELNO ... hide/show AMR boxes of those levels (or \"all\")", " {point|polygon|texture} {on|off}", " fmenu HEIGHT -or- fmenu XPOS YPOS -or- fmenu wall WALLNO", #else " readpath FILENAME.wf read Wavefront camera path (from virdir \"wfout\")", " play SPEED[f] play path (at SPEED times normal speed)", " (with \"f\" suffix, play every SPEEDth frame)", " frame FRAMENO go to Nth frame", " focal FOCALLEN focal length (determines fly/tran speed)", " clip NEAR FAR clipping distances", " jump X Y Z [RX RY RZ] put viewpoint there", " center X Y Z set center of rotation for orbit/rotate", " censize RADIUS size of center marker", " snapset filestem [frameno] set snapshot parameters", " snapshot [frameno] take snapshot [uses convert(1)]", " kira {node|ring|size|scale|span|track} starlab controls; try \"kira ?\"", #endif }; for(i = 0; i < COUNT(help1); i++) msg(help1[i], MAXVAL-1); } else if(!strcmp( argv[0], "read" )) { if(argc > 1) specks_read( &st, argv[1] ); } else if(!strcmp( argv[0], "include" )) { #ifdef NOTYET #endif } else if( !strcmp(argv[0], "on") || !strcmp(argv[0], "off") || !strcmp(argv[0], "enable") || !strcmp(argv[0], "disable") ) { st->useme = argc>1 ? getbool(argv[1], st->useme) : (argv[0][1]=='n'); msg(st->useme ? "enabled" : "disabled"); } else if(!strcmp( argv[0], "eval" ) || !strcmp(argv[0], "add")) { int k, io[2]; FILE *tf; char fdname[64+L_tmpnam]; #ifdef WIN32 tmpnam(fdname); tf = fopen(fdname, "w"); #else /* unix */ pipe(io); sprintf(fdname, "/dev/fd/%d", io[0]); tf = fdopen(io[1], "w"); #endif if(tf == NULL) { fprintf(stderr, "Yeow: can't make temp file?\n"); } else { for(k = 1; k < argc; k++) fprintf(tf, "%s ", argv[k]); fprintf(tf, "\n"); fclose(tf); specks_read( &st, fdname ); #ifdef WIN32 unlink(fdname); #endif } #ifndef WIN32 close(io[0]); #endif } else if(!strcmp( argv[0], "async" )) { char tbuf[5120]; tbuf[0] = '\0'; for(i = 1; i < argc; i++) sprintf(tbuf+strlen(tbuf), " %s", argv[i]); specks_add_async( st, tbuf ); } else if(!strcmp( argv[0], "update" )) { parti_update(); } else if(!strcmp( argv[0], "hist" )) { register struct specklist *sl; register struct speck *sp; Point xmin,xmax, mean, mid, radius; int nspecks, nclipped, nthreshed, nlow, nhigh, nundefined; int clipping = (st->clipbox.level > 0); int threshing = st->usethresh & P_USETHRESH; int nbuckets = 11; int dolog = 0; int *bucket; float v, vmin, vmax, vrange; int histvar; int i, k, bno; struct valdesc *vd; for(i=1,k=1; i+1 < argc; k++) { int yes = argv[i][0] == '-'; if(argv[i][0] != '-' && argv[i][1] != '+') break; switch(argv[i][k]) { case 't': threshing = yes ? THRESHBIT : 0; break; case 'c': case 'b': clipping = yes; break; case 'n': sscanf(argv[i][++k] ? &argv[i][k] : argv[++i], "%d", &nbuckets); i++; k=0; break; case 'l': dolog = yes; break; case '\0': i++; k=0; } } if(!specks_set_byvariable( st, argv[i], &histvar )) { msg("hist %s: expected name/index of data variable", argv[i]); return 1; } vd = &st->vdesc[st->curdata][histvar]; vmin = vd->min; vmax = vd->max; if(i+1<argc) sscanf(argv[i+1], "%f", &vmin); if(i+2<argc) sscanf(argv[i+2], "%f", &vmax); if(vmax < vmin) v = vmax, vmax = vmin, vmin = v; if(dolog && (vmin <= 0 || vmax <= 0)) { msg("hist: can't take logs (-l) if range includes zero!"); dolog = 0; } if(dolog) { vmin = log(vmin); vmax = log(vmax); } vrange = (nbuckets-1) / ((vmax>vmin) ? vmax - vmin : 1); if(nbuckets<=0 || nbuckets>20000) { msg("hist -n %d: Incredible number of histogram buckets", nbuckets); return -1; } bucket = NewA(int, nbuckets); memset(bucket, 0, nbuckets*sizeof(int)); nspecks = nclipped = nthreshed = nundefined = nlow = nhigh = 0; for(sl = st->sl; sl != NULL; sl = sl->next) { if((sp = sl->specks) == NULL || sl->text != NULL) continue; nspecks += sl->nspecks; if(sl->bytesperspeck < SMALLSPECKSIZE(histvar+1)) { nundefined += sl->nspecks; continue; } for(i = sl->nspecks; --i >= 0; sp = NextSpeck( sp, sl, 1 )) { if(clipping && (sp->p.x[0] < st->clipbox.p0.x[0] || sp->p.x[0] > st->clipbox.p1.x[0] || sp->p.x[1] < st->clipbox.p0.x[1] || sp->p.x[1] > st->clipbox.p1.x[1] || sp->p.x[2] < st->clipbox.p0.x[2] || sp->p.x[2] > st->clipbox.p1.x[2])) { nclipped++; continue; } if(threshing & sp->rgba) { nthreshed++; continue; } v = sp->val[histvar]; if(dolog) { if(v <= 0) { nundefined++; continue; } v = log(v); } bno = (int) ((v - vmin)*vrange); if(bno < 0) nlow++; else if(bno >= nbuckets) nhigh++; else bucket[bno]++; } } if(nspecks == 0) { msg("No specks loaded yet"); } else { msg("hist -n %d %s%s%s%d(%s) %g %g => ", nbuckets, dolog?"-l ":"", clipping?"-c ":"", threshing?"-t ":"", histvar, vd->name, dolog ? exp(vmin) : vmin, dolog ? exp(vmax) : vmax); msg("Total %d, %d < min, %d > max, %d undefined, %d clipped, %d threshed", nspecks, nlow, nhigh, nundefined, nclipped, nthreshed); k = nlow; msg("%d\t< %g", nlow, dolog ? exp(vmin) : vmin); for(i = 0; i < nbuckets; i++) { v = vmin + ( (vrange>0) ? i / vrange : 0 ); msg("%d\t>= %g", bucket[i], dolog ? exp(v) : v); } msg("%d\t> %g", nhigh, dolog ? exp(vmax) : vmax); } } else if(!strcmp( argv[0], "bound" )) { register struct specklist *sl; register struct speck *sp; Point xmin,xmax, mean, mid, radius; int minmaxk = MAXVAL+1, maxmaxk = 0; int nspecks = 0; xmin.x[0] = xmin.x[1] = xmin.x[2] = 1e38; xmax.x[0] = xmax.x[1] = xmax.x[2] = -1e38; mean.x[0] = mean.x[1] = mean.x[2] = 0; for(sl = st->sl; sl != NULL; sl = sl->next) { int i, maxk; if((sp = sl->specks) == NULL) continue; for(maxk=0; maxk<MAXVAL && SMALLSPECKSIZE(maxk)<sl->bytesperspeck; maxk++) ; if(minmaxk > maxk) minmaxk = maxk; if(maxmaxk < maxk) maxmaxk = maxk; for(i = sl->nspecks; --i >= 0; sp = NextSpeck( sp, sl, 1 )) { if(xmin.x[0] > sp->p.x[0]) xmin.x[0] = sp->p.x[0]; if(xmax.x[0] < sp->p.x[0]) xmax.x[0] = sp->p.x[0]; mean.x[0] += sp->p.x[0]; if(xmin.x[1] > sp->p.x[1]) xmin.x[1] = sp->p.x[1]; if(xmax.x[1] < sp->p.x[1]) xmax.x[1] = sp->p.x[1]; mean.x[1] += sp->p.x[1]; if(xmin.x[2] > sp->p.x[2]) xmin.x[2] = sp->p.x[2]; if(xmax.x[2] < sp->p.x[2]) xmax.x[2] = sp->p.x[2]; mean.x[2] += sp->p.x[2]; } nspecks += sl->nspecks; } if(nspecks == 0) { msg("No specks loaded yet"); } else { vscale( &mean, 1.0/nspecks, &mean ); vcomb( &mid, .5,&xmin, .5,&xmax ); vcomb( &radius, .5,&xmax, -.5,&xmin ); msg( "%d specks in range %g %g %g .. %g %g %g", nspecks, xmin.x[0],xmin.x[1],xmin.x[2], xmax.x[0],xmax.x[1],xmax.x[2]); msg( "midbbox %g %g %g boxradius %g %g %g", mid.x[0],mid.x[1],mid.x[2], radius.x[0],radius.x[1],radius.x[2]); msg( "mean %g %g %g", mean.x[0],mean.x[1],mean.x[2] ); } } else if(!strcmp( argv[0], "fspeed" )) { if(argc>1) { specks_set_fspeed( st, getfloat(argv[1], st->fspeed) ); st->playnext = 0.0; } msg("fspeed %g steps per real-time second", st->fspeed); } else if(!strcmp( argv[0], "speed" )) { if(argc>1) specks_set_speed( st, getfloat(argv[1], clock_speed(st->clk)) ); msg("speed %g steps per anim second", clock_speed(st->clk)); } else if(!strcmp( argv[0], "run" )) { parti_set_running( st, getbool( argv[1], 1 ) ); st->playnext = 0.0; } else if(!strcmp( argv[0], "depthsort" )) { if(argc > 1) st->depthsort = getbool(argv[1], st->depthsort); msg("depthsort %s", st->depthsort ? "on" : "off" ); } else if(!strcmp( argv[0], "fade" )) { char *fmt = "fade what?"; if(argc>1) { if(!strncmp(argv[1],"sph",3) || !strncmp(argv[1],"rad",3)) st->fade = F_SPHERICAL; else if(!strncmp(argv[1],"pla",3)) st->fade = F_PLANAR; else if(!strncmp(argv[1],"con",3) || !strncmp(argv[1],"ort",3)) st->fade = F_CONSTANT; else if(!strncmp(argv[1],"knee",3)) st->fade = F_KNEE12; else if(!strncmp(argv[1],"lin",3)) st->fade = F_LINEAR; else { msg("fade {sph|planar|const|linear|knees}"); return -1; } } if(argc>2) sscanf(argv[2], "%f", &st->fadeknee2); if(argc>3) sscanf(argv[3], "%f", &st->knee2steep); if(argc>4) sscanf(argv[4], "%f", &st->fadeknee1); if(argc>5) sscanf(argv[5], "%f%*c%f%*c%f", &st->fadecen.x[0], &st->fadecen.x[1], &st->fadecen.x[2]); if(argc>6) sscanf(argv[6], "%f", &st->fadecen.x[1]); if(argc>7) sscanf(argv[7], "%f", &st->fadecen.x[2]); switch(st->fade) { case F_SPHERICAL: fmt = "fade spherical (1/r^2 from eyepoint)"; break; case F_PLANAR: fmt = "fade planar (1/r^2 from eye plane)"; break; case F_CONSTANT: fmt = "fade const %g (as if seen at given dist)"; break; case F_LINEAR: fmt = "fade linear %g (1/r, scaled to match planar at dist)"; break; case F_KNEE12: fmt = "fade knees %g %g %g (fardist, steepness, neardist)"; break; case F_LREGION: fmt = "fade lregion %g %g %g %g %g %g (refdist; steepness, Rregion)"; break; } msg(fmt, st->fadeknee2, st->knee2steep, st->fadeknee1, st->fadecen.x[0], st->fadecen.x[1], st->fadecen.x[2]); } else if(!strcmp( argv[0], "clipbox" ) || !strcmp( argv[0], "cb" )) { Point cen, rad; int k; switch(argc) { case 2: st->clipbox.level = getbool(argv[1], st->clipbox.level); break; case 3: if(3==sscanf(argv[1], "%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2]) && 0 < (k = sscanf(argv[2], "%f%*c%f%*c%f", &rad.x[0],&rad.x[1],&rad.x[2]))) { if(k==1) rad.x[1] = rad.x[2] = rad.x[0]; vsub( &st->clipbox.p0, &cen, &rad ); vadd( &st->clipbox.p1, &cen, &rad ); st->clipbox.level = 1; /* activate */ } else { msg("clipbox: xmin,xmax ymin,ymax zmin,zmax or cenx,y,z radiusx,y,z"); } break; case 4: if(2 == sscanf(argv[1], "%f%*c%f", &cen.x[0], &rad.x[0]) && 2 == sscanf(argv[2], "%f%*c%f", &cen.x[1], &rad.x[1]) && 2 == sscanf(argv[3], "%f%*c%f", &cen.x[2], &rad.x[2])) { st->clipbox.p0 = cen; st->clipbox.p1 = rad; st->clipbox.level = 1; /* activate */ } else { msg("clipbox: xmin,xmax ymin,ymax zmin,zmax or cenx,y,z radiusx,y,z"); } break; case 7: for(k = 0; k < 3; k++) { if(sscanf(argv[k+1], "%f", &st->clipbox.p0.x[k]) <= 0 || sscanf(argv[k+4], "%f", &st->clipbox.p1.x[k]) <= 0) break; } if(k == 3) { st->clipbox.level = 1; } else { msg("clipbox: xmin ymin zmin xmax ymax zmax"); } break; } vcomb( &cen, .5, &st->clipbox.p0, .5, &st->clipbox.p1 ); vcomb( &rad, -.5, &st->clipbox.p0, .5, &st->clipbox.p1 ); msg("clipbox %s (%g,%g %g,%g %g,%g or %g,%g,%g %g,%g,%g)", st->clipbox.level ? "on":"off", st->clipbox.p0.x[0], st->clipbox.p1.x[0], st->clipbox.p0.x[1], st->clipbox.p1.x[1], st->clipbox.p0.x[2], st->clipbox.p1.x[2], cen.x[0],cen.x[1],cen.x[2], rad.x[0],rad.x[1],rad.x[2]); } else if(!strcmp( argv[0], "object" ) || sscanf( argv[0], "g%d", &i ) > 0) { struct stuff *tst = st; int a = argv[0][0]=='g' ? 0 : 1; char *ep; i = parti_object( argv[a], &tst ); ep = strchr(argv[a], '='); if(i<0) { /* never mind */ } else if(ep) { parti_set_alias( tst, ep+1 ); msg("g%d = %s", i, ep+1); } else if(a+2 == argc && argv[a+1][0] == '=') { parti_set_alias( tst, argv[a+2] ); msg("g%d = %s", i, argv[a+2]); } else if(a+1 < argc) { specks_parse_args( &tst, argc-a-1, argv+a+1 ); } else { char *alias = parti_get_alias( tst ); st = tst; msg(alias ? "object g%d[=%s] selected (%d particles)" : "object g%d%.0s selected (%d particles)", i, alias, specks_count( st->sl )); } } else if(!strcmp( argv[0], "gall" ) || !strncmp( argv[0], "allobj", 6 )) { parti_allobjs( argc-1, argv+1 ); } else if(!strcmp(argv[0], "tfm")) { int inv = 0; int mulWorldside = 0, mulObjside = 0; int hpr = 0; int calc = 0; int any, more, a0; float scl; Matrix ot, t; Point xyz; float aer[3]; char *key; i = 1; key = argv[i]; if(key == NULL) key = ""; for(more = 1; *key != '\0' && more; key++) { switch(*key) { case '=': break; case '*': mulWorldside = 1; break; case '/': inv = 1; break; case 'h': case 'p': case 'r': hpr = 1; break; case ' ': case '\t': break; case '\0': if(i < argc-1) { key = argv[++i]; break; } /* else fall into default */ default: more = 0; key--; break; } } parti_geto2w( st, parti_object( NULL, &st ), &ot ); a0 = i; argv[a0] = key; for(any = 0; any < 16 && a0+any<argc && sscanf(argv[a0+any], "%f", &t.m[any]) > 0; any++) ; switch(any) { case 1: scl = t.m[0]; t = Tidentity; t.m[0*4+0] = t.m[1*4+1] = t.m[2*4+2] = scl; break; case 6: case 7: /* ignore fovy if included */ xyz = *(Point *)&t.m[0]; if(hpr) { aer[0] = t.m[3], aer[1] = t.m[4], aer[2] = t.m[5]; } else { /* Note we permute: px py pz rx ry rz == px py pz e a r */ aer[1] = t.m[3], aer[0] = t.m[4], aer[2] = t.m[5]; } xyzaer2tfm( &t, &xyz, aer ); break; case 16: break; case 0: t = ot; break; default: msg("Usage: tfm [*] [/] [\"hpr\"] [16 numbers or 6 numbers] [=]"); return -1; } for(i = a0+any-1; i < argc; i++) { if(strchr(argv[i], '=')) calc = 1; if(strchr(argv[i], '*')) mulObjside = 1; } if(any) { if(inv) eucinv( &t, &t ); if(mulWorldside) mmmul( &t, &ot, &t ); if(mulObjside) mmmul( &t, &t, &ot ); /* with trailing '=', just print result without assignment */ if(!calc) parti_seto2w( st, parti_object( NULL, &st ), &t ); } tfm2xyzaer( &xyz, aer, &t ); msg("obj2w: x y z %s %g %g %g %g %g %g", hpr?"h p r":"rx ry rz", xyz.x[0],xyz.x[1],xyz.x[2], aer[1-hpr],aer[hpr],aer[2] ); for(i = 0; i < 3; i++) msg(" %10.7f %10.7f %10.7f %10.7f", t.m[i*4+0],t.m[i*4+1],t.m[i*4+2],t.m[i*4+3]); msg(" %10.7g %10.7g %10.7g %10.7g", t.m[3*4+0],t.m[3*4+1],t.m[3*4+2],t.m[3*4+3]); eucinv( &t, &t ); tfm2xyzaer( &xyz, aer, &t ); msg("w2obj: x y z %s %g %g %g %g %g %g", hpr?"h p r":"rx ry rz", xyz.x[0],xyz.x[1],xyz.x[2], aer[1-hpr],aer[hpr],aer[2] ); for(i = 0; i < 3; i++) msg(" %10.7f %10.7f %10.7f %10.7f", t.m[i*4+0],t.m[i*4+1],t.m[i*4+2],t.m[i*4+3]); msg(" %10.7g %10.7g %10.7g %10.7g", t.m[3*4+0],t.m[3*4+1],t.m[3*4+2],t.m[3*4+3]); } else if(!strcmp( argv[0], "bgcolor" )) { char *result, given[64]; if(argc == 4) { sprintf(given, "%.10s,%.10s,%.10s", argv[1],argv[2],argv[3]); result = parti_bgcolor( given ); } else { result = parti_bgcolor( argv[1] ); } msg("bgcolor %s", result); #if !CAVEMENU /* virdir emulation */ } else if(!strcmp( argv[0], "stereo" )) { for(i = 1; i < argc; i++) parti_stereo( argv[i] ); msg("stereo %s", parti_stereo(NULL)); } else if(!strncmp( argv[0], "snapset", 7 ) || !strcmp( argv[0], "snapshot" )) { char *frameno = NULL, *basename = NULL; char *tail = NULL; if(argc > 2) frameno = argv[2]; if(argc == 2) { strtol(argv[1], &tail, 0); if(tail != argv[1] && *tail == '\0') frameno = argv[1]; } if(argc > 1 && frameno == NULL) basename = argv[1]; parti_snapset( basename, frameno ); if(0!=strncmp( argv[0], "snapset", 7 )) { char snapinfo[1024]; if(parti_snapshot(snapinfo) >= 0) msg("Snapped %s", snapinfo); } } else if(!strcmp( argv[0], "move" ) || !strcmp( argv[0], "move-objects" )) { i = parti_move( argv[1], &st ); msg(i>=0 ? "move-objects on ; g%d selected" : "move-objects off", i); } else if(!strcmp( argv[0], "clip" )) { msg("%s", parti_clip( argv[1], argc>2?argv[2]:NULL ) ); } else if(!strcmp( argv[0], "ortho" )) { #ifdef NOTYET int ortho = parti_ortho( argc>1 ? argv[1] : NULL); float fov = parti_fov(NULL); msg(ortho ? "ortho on (fov %g units)" : "ortho off (fov %g degrees)" #endif } else if(!strcmp( argv[0], "pickrange" )) { float pickrange = parti_pickrange( argv[1] ); msg("pickrange %g", parti_pickrange( NULL )); } else if(!strcmp( argv[0], "fov" ) || !strcmp( argv[0], "fovy" )) { float fovy = parti_fovy( (argc>1) ? argv[1] : NULL ); msg("fovy %g", fovy); } else if(!strncmp( argv[0], "focal", 5 )) { float focallen = parti_focal( (argc>1) ? argv[1] : NULL ); msg("focallength %g", focallen); } else if(!strncmp( argv[0], "jump", 4 )) { Point xyz; float aer[3]; static int stupid[3] = {1,0,2}; /* aer -> rx ry rz */ Matrix c2w; parti_getc2w( &c2w ); tfm2xyzaer( &xyz, aer, &c2w ); if(argc>1) { for(i=1; i<argc && i<4+3; i++) { if(i-1<3) xyz.x[i-1] = getfloat(argv[i], xyz.x[i-1]); else aer[stupid[i-4]] = getfloat(argv[i], aer[stupid[i-4]]); } xyzaer2tfm( &c2w, &xyz, aer ); parti_setc2w( &c2w ); } msg("jump %g %g %g %g %g %g (XYZ RxRyRz)", xyz.x[0],xyz.x[1],xyz.x[2], aer[1],aer[0],aer[2]); } else if((!strcmp( argv[0], "rdata" ) || !strcmp(argv[0], "readpath")) && argc>1) { char *tfname = argv[1]; char *realfile = findfile( NULL, tfname ); if(realfile == NULL) { tfname = (char *)alloca(strlen(argv[1]) + 32); sprintf(tfname, "data/record/%s%s", argv[1], strstr(argv[1], ".wf") ? "" : ".wf"); realfile = findfile( NULL, tfname+12 ); } if(realfile == NULL) realfile = findfile( NULL, tfname ); if(realfile) parti_readpath( st, realfile ); else msg("%s: can't find \"%s\" nor data/record/... nor ...wf", argv[0],argv[1]); } else if(!strcmp( argv[0], "play" )) { parti_play( st, argc>1 ? argv[1] : NULL ); } else if(!strcmp( argv[0], "frame" )) { struct wfpath *pp; i = parti_frame( st, argv[1], &pp ); msg("frame %d (of %d..%d)", pp->curframe, pp->frame0, pp->nframes + pp->frame0 - 1); } else if(!strcmp( argv[0], "interest") || !strcmp( argv[0], "int" ) || !strcmp( argv[0], "center" ) || !strcmp( argv[0], "cen" )) { Point cen; float censize = parti_getcensize(); parti_getcenter( &cen ); if(argc > 1) { sscanf(argv[1], "%f%*c%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2], &censize); if(argc > 3) for(i=0;i<3;i++) cen.x[i] = getfloat(argv[i+1], cen.x[i]); if(argc == 3 || argc == 5) sscanf(argv[argc-1], "%f", &censize); parti_center( &cen ); if(censize != parti_getcensize()) parti_censize( censize ); } #if CAVEMENU msg("center %g %g %g", cen.x[0],cen.x[1],cen.x[2]); #else msg("center %g %g %g %g(radius)", cen.x[0],cen.x[1],cen.x[2], censize); #endif } else if(!strcmp( argv[0], "censize" )) { if(argc>1) parti_censize( getfloat(argv[1], parti_getcensize())); msg("censize %g (interest-marker size)", parti_getcensize()); #endif /*non-CAVEMENU*/ } else if(!strcmp( argv[0], "step" ) #if !CAVE || !strcmp( argv[0], "time" ) #endif ) { if(argc>1) { if(argv[1][0] == '+') { if(argv[1][1] != '\0') clock_set_step( st->clk, getfloat(&argv[1][1], 0) ); clock_step( st->clk, 1 ); } else if(!strcmp(argv[1], "-")) { clock_step( st->clk, -1 ); } else { clock_set_time( st->clk, getfloat(argv[1], 0) ); } } #if !CAVEMENU parti_set_running( st, 0 ); specks_set_timestep(st); #endif msg("step %g", st->currealtime); } else if(!strcmp( argv[0], "trange" ) || !strcmp( argv[0], "steprange" )) { double utmin = st->utmin; double utmax = st->utmax; double utwrap = st->utwrap; if(argc>1) { if(!strcmp(argv[1],"on")) st->usertrange = 1; else if(!strcmp(argv[1],"off")) st->usertrange = 0; else { st->utmin = getfloat( argv[1], utmin ); if(argc>2) st->utmax = getfloat( argv[2], utmax ); if(argc>3) st->utwrap = getfloat( argv[3], utwrap ); if(utmin != st->utmin || utmax != st->utmax) st->usertrange = 1; } } msg(st->usertrange ? "trange %lg %lg %lg (tmin tmax twrap)" : "trange off (%lg %lg %lg tmin tmax twrap)", st->utmin, st->utmax, st->utwrap); } else if(!strcmp( argv[0], "fwd" )) { if(argc>1) clock_set_fwd( st->clk, getbool(argv[1], 1) ); IFMENU( set_fwd( clock_fwd(st->clk), ment.fwd, st ) ); } else if(!strcmp( argv[0], "gscale" )) { if(argc>1) sscanf(argv[1], "%f", &st->gscale); msg("gscale %g (scaling particles), then translating by %g %g %g", st->gscale, st->gtrans.x[0],st->gtrans.x[1],st->gtrans.x[2]); } else if(!strcmp( argv[0], "clearobj" )) { struct specklist **slp; slp = specks_timespecksptr( st, st->curdata, st->curtime ); specks_discard( st, slp ); st->sl = *slp; } else if(!strcmp( argv[0], "every" )) { if(argc>1) sscanf(argv[1], "%d", &st->subsample); if(st->subsample <= 0) st->subsample = 1; msg("display every %dth particle (of %d)", st->subsample, specks_count(st->sl)); } else if(!strncmp( argv[0], "everycomp", 9 )) { st->everycomp = getbool(argv[1], st->everycomp); msg("everycomp %d (%s compensate for \"every\" subsampling)", st->everycomp, st->everycomp ? "do" : "don't"); } else if(!strncmp( argv[0], "color", 5 )) { struct valdesc *vd; if(argc>1) { if(!specks_set_byvariable( st, argv[1], &st->coloredby )) st->coloredby = CONSTVAL; } vd = &st->vdesc[st->curdata][st->coloredby]; if(argc>2 && (!strncmp(argv[2], "int", 3) || !strncmp(argv[2], "exact", 5))) { vd->call = 0; vd->cexact = 1; vd->cmin = 0; argc--, argv++; } if(argc>2 && (!strncmp(argv[2], "cont", 4) || !strcmp(argv[2], "-exact"))) { vd->cexact = 0; argc--, argv++; } if(argc>2 && sscanf(argv[2], "%f", &vd->cmin)) vd->call = 0; if(argc>3 && sscanf(argv[3], "%f", &vd->cmax)) vd->call = 0; if((vd->cmin == vd->cmax && vd->cmin == 0) || (argc>2 && (!strcmp(argv[2],"-") || !strcmp(argv[2],"all")))) { vd->cmin = vd->min, vd->cmax = vd->max, vd->call = 1; } if(argc>4 && st->coloredby == CONSTVAL) sscanf(argv[4], "%f", &vd->mean); if(st->coloredby == CONSTVAL) { msg("coloring-by rgb %.3f %.3f %.3f", vd->cmin, vd->cmax, vd->mean); } else if(vd->cexact) { msg("coloring-by %d(%s) exactly (cindex=data+%g; data %g..%g, cmap 0..%d)", st->coloredby, vd->name, vd->cmin, vd->min,vd->max, st->ncmap-1); } else { if(vd->min == vd->cmin && vd->max == vd->cmax || vd->nsamples==0) { msg("coloring-by %d(%s) %g.. %g mean %.3g]", st->coloredby, vd->name, vd->cmin, vd->cmax, vd->mean); } else { msg("coloring-by %d(%s) %g.. %g (data %g..%g mean %.3g)", st->coloredby, vd->name, vd->cmin, vd->cmax, vd->min, vd->max, vd->mean); } } if(argc>1) st->colorseq++; } else if(!strcmp( argv[0], "datavar" ) || !strcmp( argv[0], "dv" )) { int min = 0, max = CONSTVAL-1; if(argc>1 && specks_set_byvariable( st, argv[1], &min )) max = min; if(st->ndata>1) msg("In dataset %d %s:", st->curdata, st->dataname[st->curdata]); for(i = min; i <= max; i++) { struct valdesc *vd = &st->vdesc[st->curdata][i]; if(max>min && vd->name[0] == '\0' && vd->max == vd->min) continue; msg("datavar %d %s %g.. %g mean %g%s%s", i, vd->name[0]=='\0'?"\"\"" : vd->name, vd->min, vd->max, vd->mean, st->sizedby==i ? "[lum]" : "", st->coloredby==i ? "[color]" : ""); } } else if(!strcmp( argv[0], "datawait" )) { if(argc>1) { switch(i = getbool(argv[1],-1)) { case 0: case 1: st->datasync = i; break; } msg("datawait %s", st->datasync ? "on" : "off"); } specks_datawait(st); } else if(!strncmp(argv[0], "kira", 4)) { char *swhat = argv[1]; char *sval = argc>2 ? argv[2] : NULL; int what; double val; #ifndef USE_KIRA msg("Need to recompile with -DUSE_KIRA"); #else if(swhat == NULL) swhat = "?"; if(!strncmp(swhat, "sep", 3) || !strncmp(swhat, "semi", 4)) { kira_set( st, KIRA_RINGSIZE, swhat[2]=='p' ? KIRA_RINGSEP : KIRA_RINGA ); msg("kiractl ringsize %s", kira_get( st, KIRA_RINGSIZE ) == KIRA_RINGSEP ? "separation" : "semimajor"); } else if(!strcmp(swhat, "ringsize") || !strcmp(swhat, "ringscale") || !strcmp(swhat, "size")) { if(sval) { val = !strncmp(sval,"sep",3) ? KIRA_RINGSEP : !strncmp(sval,"semi",4) ? KIRA_RINGA : !strcmp(sval,"a") ? KIRA_RINGA : kira_get( st, KIRA_RINGSIZE ); kira_set( st, KIRA_RINGSIZE, val ); if(argc > 3) kira_set( st, KIRA_RINGSCALE, getfloat( argv[3], kira_get( st, KIRA_RINGSCALE ) ) ); } msg("kiractl ringsize %s %g", kira_get( st, KIRA_RINGSIZE ) == KIRA_RINGSEP ? "separation" : "semimajor", kira_get( st, KIRA_RINGSCALE )); } else if(!strcmp(swhat, "ringscale") || !strcmp(swhat, "scale")) { if(sval) kira_set( st, KIRA_RINGSCALE, getfloat( sval, kira_get(st,KIRA_RINGSCALE) ) ); msg("kiractl ringscale %g", kira_get(st,KIRA_RINGSCALE)); } else if(!strcmp(swhat, "span") || !strcmp(swhat, "ringspan")) { if(argc>2 && (val = getfloat( argv[2], -1 )) >= 0) kira_set( st, KIRA_RINGMIN, val ); if(argc>3 && (val = getfloat( argv[3], -1 )) >= 0) kira_set( st, KIRA_RINGMAX, val ); msg("kiractl ringspan %.0f %.0f", kira_get( st, KIRA_RINGMIN ), kira_get( st, KIRA_RINGMAX )); } else if(!strncmp(swhat, "nod", 3) || !strncmp(swhat, "ring", 4)) { what = swhat[0]=='n' ? KIRA_NODES : KIRA_RINGS; if(sval) kira_set( st, what, (0==strncmp(sval, "root", 4)) ? KIRA_ROOTS : getbool(sval, KIRA_ON) ); val = kira_get( st, what ); msg("kiractl %s %s %g", what==KIRA_NODES ? "nodes" : "rings", val==2 ? "root" : val==1 ? "on" : "off", kira_get( st, KIRA_TICKSCALE )); } else if(!strncmp(swhat, "tree", 3) || !strcmp(swhat, "arc")) { if(sval) kira_set( st, KIRA_TREE, sval[0]=='c' ? KIRA_CROSS : sval[0]=='t' ? KIRA_TICK : getbool(sval, KIRA_ON) ); if(argc>3 && sscanf(argv[3], "%lf", &val)>0) kira_set( st, KIRA_TICKSCALE, val ); val = kira_get( st, KIRA_TREE ); msg("kiractl tree %s", val==KIRA_CROSS ? "cross" : val==KIRA_TICK ? "tick" : val ? "on" : "off"); } else if(!strncmp(swhat, "track", 2)) { if(sval) kira_set( st, KIRA_TRACK, getbool(sval, 0) ); val = kira_get( st, KIRA_TRACK ); msg(val == 0 ? "kiractl track off" : "kiractl track %d", (int)val); } else { msg("kiractl {node|ring} {on|off|root} | tree {on|off|cross|tick} [<tickscale>] | size {sep|semimaj} | scale <fac> | span <minpix> <maxpix> | track <id>"); } #endif } else if(!strncmp( argv[0], "lum", 3 )) { struct valdesc *vd; if(argc>1) { if(!specks_set_byvariable( st, argv[1], &st->sizedby )) st->sizedby = CONSTVAL; } vd = &st->vdesc[st->curdata][st->sizedby]; if(argc>2) vd->lall = 0, vd->lmin = getfloat(argv[2], vd->lmin); if(argc>3) vd->lall = 0, vd->lmax = getfloat(argv[3], vd->lmax); if(vd->lmin == vd->lmax && vd->lmin == 0 || argc>2 && (!strcmp(argv[2],"-") || !strcmp(argv[2],"all"))) { vd->lmin = vd->min, vd->lmax = vd->max, vd->lall = 1; } if(st->sizedby == CONSTVAL) { msg("lum-by constant %g", vd->lmin); } else { msg("lum-by %d (%s) [%g..%g mean %.3g]", st->sizedby, vd->name, vd->lmin, vd->lmax, vd->mean); } if(argc>1) st->sizeseq++; } else if(!strcmp( argv[0], "cmap" ) || !strcmp( argv[0], "boxcmap" ) || !strcmp( argv[0], "textcmap" )) { char *tfname = argv[1]; char *realfile = findfile( NULL, argv[1] ); if(argc != 2) { msg("Usage: %s <ascii-file-of-RGB-triples>", argv[0]); } else { if(argc > 1 && realfile == NULL) { tfname = (char *)alloca( strlen(argv[1]) + 6 ); sprintf(tfname, "%s.cmap", argv[1]); realfile = findfile( NULL, tfname ); } if(realfile != NULL) { if(argv[0][0] == 'b') { specks_read_cmap( st, realfile, &st->boxncmap, &st->boxcmap ); } else if(argv[0][0] == 't') { specks_read_cmap( st, realfile, &st->textncmap, &st->textcmap ); } else { specks_read_cmap( st, realfile, &st->ncmap, &st->cmap ); st->colorseq++; /* Must recompute particle coloring */ } } else { msg("%s: can't find \"%s\" nor ....cmap", argv[0], argv[1]); } } } else if(!strncmp( argv[0], "cment", 5 ) || !strncmp( argv[0], "boxcment", 8 ) || !strncmp( argv[0], "textcment", 6 )) { int i, r,g,b; unsigned char *cvp; char *prefix = ""; int ncmap = st->ncmap; struct cment *cmap = st->cmap; switch(argv[0][0]) { case 'b': prefix = "box"; ncmap = st->boxncmap; cmap = st->boxcmap; break; case 't': prefix = "text"; ncmap = st->textncmap; cmap = st->textcmap; break; } if(argc>1) { i = atoi(argv[1]); if(i < 0 || i > ncmap) { msg("cment: %d out of range (must be 0..%d)", i, ncmap-1); } else { if(argc>4) { r=255*atof(argv[2]); g=255*atof(argv[3]); b=255*atof(argv[4]); cmap[i].raw = PACKRGBA(r,g,b,0); cmap[i].cooked = specks_cookcment( st, cmap[i].raw ); if(st->sl) st->colorseq++; /* force recolor */ } cvp = (unsigned char *)&cmap[i].raw; msg( "%scment %d %.3f %.3f %.3f (of cmap 0..%d", prefix, i, cvp[0]/255., cvp[1]/255., cvp[2]/255., ncmap-1 ); } } else { msg("Usage: %scment colorindex [r g b]", prefix); } } else if(!strncmp( argv[0], "only", 4 )) { int plus = !strcmp(argv[0], "only+"); int minus = !strcmp(argv[0], "only-"); int exact = !strcmp(argv[0], "only="); int total = 0, chosen = 0, waschosen = 0; int tvar = -1; int all = 0; int k; int nspecks, nvisible, needthresh; struct specklist *sl; struct speck *sp; if(argc>1) { if(!strcmp(argv[1], "all") || !strcmp(argv[1], "*")) all = 1; if(!strcmp(argv[1], "none")) all = -1; } if(!all && ((!plus && !minus && !exact) || !specks_set_byvariable( st, argv[1], &tvar))) { msg("only+/only-/only= \"all\"|\"none\"|varname value minvalue-maxvalue ..."); } plus |= exact; if(exact || all) { /* preset all values */ int wipebit = (plus && all>0) ? 0 : THRESHBIT; for(sl = st->sl; sl != NULL; sl = sl->next) { if((sp = sl->specks) == NULL || sl->text != NULL) continue; for(k = sl->nspecks; --k >= 0; sp = NextSpeck(sp, sl, 1)) sp->rgba = (sp->rgba & ~THRESHBIT) | wipebit; } } /* * If a thresh variable is given, use "plus" to choose * whether to make that speck visible. * NOTE that specks with THRESHBIT on are *invisible*! */ for(i = 2; i < argc; i++) { float vmin = -1e38, vmax = 1e38; if(argv[i][0] == '<') vmax = atof(argv[i]+1); else if(argv[i][0] == '>') vmin = atof(argv[i]+1); else { switch(sscanf(argv[i], "%f%*c%f", &vmin, &vmax)) { case 1: vmax = vmin; break; case 2: break; default: msg("%s: \"%s\": expected N or <N or >N or N-M", argv[0], argv[i]); return -1; } } for(sl = st->sl; sl != NULL; sl = sl->next) { if((sp = sl->specks) == NULL || sl->text != NULL || tvar<0 || SMALLSPECKSIZE(tvar+1) > sl->bytesperspeck) continue; for(k = sl->nspecks; --k >= 0; sp = NextSpeck(sp, sl, 1)) { if(sp->val[tvar]>=vmin && sp->val[tvar]<=vmax) sp->rgba = plus ? sp->rgba & ~THRESHBIT : sp->rgba | THRESHBIT; } } } nspecks = nvisible = 0; for(sl = st->sl; sl != NULL; sl = sl->next) { if((sp = sl->specks) == NULL || sl->text != NULL) continue; nspecks += sl->nspecks; for(k = sl->nspecks; --k >= 0; sp = NextSpeck(sp, sl, 1)) { if(!(sp->rgba & THRESHBIT)) nvisible++; } } needthresh = (st->usethresh&P_USETHRESH)==0 && nvisible < nspecks; if(needthresh) st->usethresh |= P_USETHRESH; msg("only %d visible of %d%s", nvisible, nspecks, needthresh ? " (selecting \"thresh on\")" : ""); } else if(!strncmp( argv[0], "thresh", 6 )) { int i, expect = -1; float v; char smin[16], smax[16], *arg; int changed = 0; for(i = 1; i < argc; i++) { arg = argv[i]; if(!strcmp(arg,"off")) st->usethresh &= ~P_USETHRESH, expect = -1; else if(!strcmp(arg,"on")) st->usethresh |= P_USETHRESH, expect = -1; else if(expect<0 && specks_set_byvariable(st, arg, &st->threshvar)) { expect = 0; st->usethresh = P_USETHRESH; /* neither MIN nor MAX set */ changed = 1; } else if(!strcmp(arg,"min") || !strcmp(arg, ">")) expect = 0; else if(!strcmp(arg,"max") || !strcmp(arg, "<")) expect = 1; else if(!strcmp(arg,"=")) expect = 2; else if(!strcmp(arg, "-")) { st->usethresh &= expect ? ~P_THRESHMAX : ~P_THRESHMIN; expect = 1; } else if(sscanf(arg, "%f", &v) > 0) { if(expect != 1) { /* -1, 0, or 2 */ st->thresh[0] = v; st->usethresh |= P_THRESHMIN | P_USETHRESH; changed = 1; } if(expect >= 1) { /* 1 or 2 */ st->thresh[1] = v; st->usethresh |= P_THRESHMAX | P_USETHRESH; changed = 1; } expect = 1; } else if(sscanf(arg, ">%f", &st->thresh[0]) > 0) { st->usethresh |= P_THRESHMIN | P_USETHRESH; changed = 1; } else if(sscanf(arg, "<%f", &st->thresh[1]) > 0) { st->usethresh |= P_THRESHMAX | P_USETHRESH; changed = 1; } else if(sscanf(arg, "=%f", &st->thresh[0]) > 0) { st->thresh[1] = st->thresh[0]; st->usethresh |= P_THRESHMIN | P_THRESHMAX | P_USETHRESH; changed = 1; } } if(changed) { struct specklist *sl; st->threshseq++; for(sl = st->sl; sl != NULL; sl = sl->next) specks_rethresh( st, sl, st->threshvar ); } sprintf(smin, st->usethresh&P_THRESHMIN ? "%g" : "-", st->thresh[0]); sprintf(smax, st->usethresh&P_THRESHMAX ? "%g" : "-", st->thresh[1]); msg(st->usethresh == 0 ? "thresh off" : "thresh%s %d(%s) min %s max %s", st->usethresh&P_USETHRESH ? "" : " off ", st->threshvar, st->vdesc[st->curdata][st->threshvar].name, smin, smax); } else if(!strcmp( argv[0], "rawdump" )) { FILE *f = argc>1 ? fopen(argv[1], "w") : NULL; struct specklist *sl; int i, j, nmine, net; if(f == NULL) { msg("%s: can't create", argv[1]); } else { fprintf(f, "# from dataset %d \"%s\" timestep %d\n", st->curdata, st->fname[st->curdata][st->curtime], st->curtime); for(i = 0; st->vdesc[st->curdata][i].name[0] != '\0'; i++) { fprintf(f, "%s\t", st->vdesc[st->curdata][i].name); } fprintf(f, "\n"); nmine = i; net = 0; sl = specks_timespecks( st, st->curdata, st->curtime ); if(sl == NULL && st->ntimes <= 1) sl = st->sl; for(; sl != NULL; sl = sl->next) { for(j = 0; j < sl->nspecks; j++) { for(i = 0; i < nmine; i++) fprintf(f, "%g\t", (NextSpeck(sl->specks, sl, j)->val[i])); fprintf(f, "\n"); net++; } } fclose(f); msg("Wrote %d particles (%d fields each) to %s", net, nmine, argv[1]); } } else if(!strncmp( argv[0], "slum", 3 ) || !strcmp( argv[0], "scale-lum" )) { float v, *lp; int by = st->sizedby, cd = st->curdata; if(argc>1) { if(argc>2) specks_set_byvariable( st, argv[2], &by ); if(argc>3) sscanf(argv[3], "%d", &cd); if((unsigned int)cd >= MAXFILES) cd = 0; lp = &st->vdesc[cd][by].lum; *lp = getfloat( argv[1], *lp!=0 ? *lp : 1 ); } msg("slum scaling %g (var %d %s dataset %s (%d))", st->vdesc[cd][by].lum, by, st->vdesc[cd][by].name, st->dataname[cd], cd); } else if(!strcmp( argv[0], "see" )) { if(argc>1) { int v = -1, len = strlen(argv[1]); /* search st->dataname[] array! XXX */ if(sscanf(argv[1], "%d", &v) > 0) { /* fine */ } else { for(v = st->ndata; --v >= 0; ) { if(!strncasecmp( argv[1], st->dataname[v], len )) break; } if(v < 0) { char msgstr[2048]; sprintf(msgstr, "Never heard of dataset \"%s\"; we have these %d:", argv[1], st->ndata); for(v = 0; v < st->ndata; v++) sprintf(msgstr+strlen(msgstr), " \"%s\"(%d) ", st->dataname[v], v); msg(msgstr); v = -1; } } if(v >= 0 && v < st->ndata) st->curdata = v; } msg("showing dataset %d (%s)", st->curdata, st->dataname[st->curdata]); } else if(!strcmp( argv[0], "show" ) || !strcmp( argv[0], "hide" ) || !strncasecmp( argv[0], "showbox", 7 ) || !strncasecmp( argv[0], "hidebox", 7 )) { int doshow = argv[0][0] == 's'; int i, level, mask = 0; if(doshow) st->useboxes = 1; for(i = 1; i < argc; i++) { level = getbool( argv[i], -2 ); if(level == -1) mask = ~0; else if(level >= 0) mask |= (1 << level); } if(doshow) st->boxlevelmask |= mask; else st->boxlevelmask &= ~mask; /* st->boxlevelmask &= (1 << st->boxlevels) - 1; * Don't do this -- we want "show all" to apply to levels that * we haven't read in yet. */ #if CAVEMENU for(i = 0; i < st->boxlevels && i < COUNT(ment.boxlevel); i++) { struct boxleveler blr; blr.st = st; blr.level = i; if(ment.boxlevel[i]) set_boxlevel( 0, ment.boxlevel[i], &blr ); } #endif } else if(!strcmp( argv[0], "box" ) || !strcmp( argv[0], "boxes" )) { st->useboxes = getbool( argv[1], (st->useboxes+1)%3 ); msg("boxes %s", st->useboxes==2 ? "ONLY" : st->useboxes ? "ON" : "off"); } else if(!strncmp( argv[0], "boxlabel", 6 )) { if(argc>1) st->boxlabels = getbool( argv[1], !st->boxlabels ); else st->boxlabels = !st->boxlabels; msg("boxlabels %s", st->boxlabels?"on":"off"); } else if(!strncmp( argv[0], "boxaxes", 5 )) { if(argc>1) st->boxaxes = getbool( argv[1], !st->boxaxes ); else st->boxaxes = !st->boxaxes; msg("boxaxes %s", st->boxaxes?"on":"off"); } else if(!strcmp(argv[0], "boxscale")) { char buf[512]; int from, to; if(argc>1) { float v = atof(argv[1]); from = getbool(argc>2?argv[2]:NULL, 0); to = getbool(argc>3?argv[3]:NULL, MAXBOXLEV-1); if(from<0) from = 0; if(to>MAXBOXLEV-1) to=MAXBOXLEV-1; while(from<=to) st->boxscale[from++] = v; } sprintf(buf, "boxscales:"); for(to=MAXBOXLEV-1; to>1 && st->boxscale[to]==st->boxscale[to-1]; to--) ; for(from = 0; from <= to; from++) sprintf(buf+strlen(buf), " %g", st->boxscale[from]); msg("%s", buf); } else if(!strcmp( argv[0], "go" ) || !strcmp( argv[0], "gobox" )) { int boxno = getbool( argv[1], -1 ); if(boxno < 0) { msg("Usage: gobox <boxnumber> -- sets POI and jumps to view that AMR box"); } else { specks_gobox( st, boxno, argc-2, argv+2 ); } } else if(!strcmp( argv[0], "goboxscale" )) { if(argc>1) sscanf(argv[1], "%f", &st->goboxscale); msg("goboxscale %g (\"gobox\" sets scale to %g * box diagonal; 0 => leave scale intact)", st->goboxscale, st->goboxscale); } else if(!strcmp( argv[0], "psize" ) || !strcmp(argv[0], "pointsize")) { if(argc>1) { st->psize = getfloat( argv[1], st->psize ); IFMENU( set_psize( st->psize, ment.psize, st ) ); } msg("pointsize %g pixels (times scale-lum value)", st->psize ); } else if(!strcmp( argv[0], "polysize" )) { if(argc>1) { st->polysize = getfloat(argv[1], st->polysize); IFMENU( set_polysize( st->polysize, ment.polysize, st ) ); } if(argc>2) { if(argv[2][0] == 'a' || argv[2][0] == 's') st->polyarea = 1; else if(argv[2][0] == 'r') st->polyarea = 0; } msg("polysize %g%s", st->polysize, st->polyarea ? " area":"" ); } else if(!strncmp( argv[0], "polylum", 7 )) { char *area = ""; if(argc>1) { if(!specks_set_byvariable( st, argv[1], &st->polysizevar )) if(!strcmp(argv[1], "-1") || !strcmp(argv[1], "point-size") || !strcmp(argv[1], "pointsize")) st->polysizevar = -1; if(st->polysizevar == CONSTVAL) st->polysizevar = -1; /* Can't do const */ } if(argc>2) sscanf(argv[2], "%f", &st->polysize); switch(argv[argc-1][0]) { case 'r': case 'd': case 's': st->polyarea = 0; break; case 'a': st->polyarea = 1; break; } if(st->polyarea) area = " area"; if(st->polysizevar < 0) msg("polylumvar point-size%s", area); else msg("polylumvar %d(%s)%s", st->polysizevar, st->vdesc[st->curdata][st->polysizevar].name, area); } else if(!strncmp( argv[0], "polyminpixels", 7 )) { if(argc>1) st->polymin = getfloat(argv[1], st->polymin); if(argc>2) st->polymax = getfloat(argv[2], st->polymax); msg("polyminpixels %g %g (minpixels maxpixels)", st->polymin, st->polymax ); } else if(!strncmp( argv[0], "labelminpixels", 7 )) { st->textmin = getfloat(argv[1], st->textmin); msg("labelminpixels %g", st->textmin ); } else if(!strcmp( argv[0], "labelsize" ) || !strcmp(argv[0], "lsize")) { st->textsize = getfloat(argv[1], st->textsize); msg("labelsize %g", st->textsize); } else if(!strcmp( argv[0], "point" ) || !strcmp( argv[0], "points" )) { st->usepoint = getbool( argv[1], !st->usepoint ); IFMENU( set_point( st->usepoint, ment.point, st ) ); msg("points %s", st->usepoint ? "on":"off"); } else if(!strcmp( argv[0], "poly" ) || !strncmp( argv[0], "polygon", 7 )) { st->usepoly = getbool( argv[1], !st->usepoly ); IFMENU( set_poly( st->usepoly, ment.poly, st ) ); msg("polygons %s", st->usepoly ? "on":"off"); } else if(!strncmp( argv[0], "texture", 7 ) || !strcmp( argv[0], "tx" )) { if(argc > 1 && !strcmp(argv[1], "preload")) { msg("Preloading textures..."); for(i = 0; i < st->ntextures; i++) if(st->textures[i]) txload( st->textures[i] ); } else { st->usetextures = getbool( argv[1], !st->usetextures ); msg("textures %s", st->usetextures ? "on":"off"); } } else if(!strcmp( argv[0], "txscale" )) { if(argc>1) sscanf(argv[1], "%f", &st->txscale); msg("txscale %.3f", st->txscale); } else if(!strncmp( argv[0], "polyorivar", 10)) { if(argc>1) st->polyorivar0 = getbool(argv[1], -1); msg(st->polyorivar0 >= 0 ? "polyorivar %d : polygon orientations from var %d..%d" : "polyorivar %d : polygon orientations parallel to screen", st->polyorivar0, st->polyorivar0, st->polyorivar0+5); } else if(!strcmp( argv[0], "texturevar")) { if(argc>1) st->texturevar = getbool(argv[1], -1); msg("texturevar %d", st->texturevar); } else if(!strcmp( argv[0], "label" ) || !strcmp( argv[0], "labels" )) { st->usetext = getbool( argv[1], !st->usetext ); IFMENU( set_label( st->usetext, ment.label, st ) ); msg("labels %s", st->usetext ? "on":"off"); } else if(!strcmp( argv[0], "laxes" )) { st->usetextaxes = getbool(argv[1], !st->usetextaxes); msg("laxes %s (axes on each label)", st->usetextaxes ? "on":"off"); } else if(!strncmp( argv[0], "polyside", 8 )) { if(argc>1) sscanf(argv[1], "%d", &st->npolygon); msg("polysides %d (polygons drawn with %d sides)", st->npolygon,st->npolygon); } else if(!strcmp( argv[0], "gamma" )) { if(argc>1) sscanf(argv[1], "%f", &st->gamma); msg("gamma %g", st->gamma); } else if(!strncmp( argv[0], "cgamma", 4 ) || !strncmp(argv[0], "setgamma", 6)) { float *p = NULL; int any = 0; for(i = 1; i < argc; i++) { if(!strcmp(argv[i], "-b")) i++, p = &st->rgbright[0]; else if(!strcmp(argv[i], "-g")) i++, p = &st->rgbgamma[0]; else if(i == argc-1) p = &st->rgbgamma[0]; else continue; switch(sscanf(argv[i], "%f%*c%f%*c%f", &p[0],&p[1],&p[2])) { case 1: p[1] = p[2] = p[0]; /* and fall into... */ case 3: any = 1; break; default: msg("setgamma [-b BRIGHT] [-g GAMMA]: want 1 or 3 floats not %s", argv[i]); } } msg("setgamma -b %g,%g,%g -g %g,%g,%g", st->rgbright[0],st->rgbright[1],st->rgbright[2], st->rgbgamma[0],st->rgbgamma[1],st->rgbgamma[2]); if(any) specks_rgbremap( st ); } else if(!strcmp( argv[0], "alpha" )) { if(argc>1) sscanf(argv[1], "%f", &st->alpha); IFMENU( set_alpha( st->alpha, ment.alpha, st ) ); msg("alpha %g", st->alpha); } else if(!strncmp( argv[0], "fast", 4 )) { st->fast = getbool(argv[1], !st->fast); if(argc > 2) sscanf(argv[2], "%f", &st->pfaint); if(argc > 3) sscanf(argv[3], "%f", &st->plarge); msg("fast %s (ptsize %.3g %.3g (faintest largest))", st->fast ? "on" : "off (better rendering)", st->pfaint, st->plarge); } else if(!strncmp( argv[0], "ptsize", 6 )) { if(argc > 1) sscanf(argv[1], "%f", &st->pfaint); if(argc > 2) sscanf(argv[2], "%f", &st->plarge); msg("ptsize %.3g %.3g (faintest largest)", st->pfaint, st->plarge); } else if(!strcmp( argv[0], "fog" ) && argc > 1) { IFMENU( set_fog( st->fog, ment.fog, st ) ); } else if(!strcmp( argv[0], "menu" ) || !strcmp( argv[0], "fmenu" )) { #if CAVEMENU if(argc == 4 && !strncmp(argv[1], "pos", 3)) { menu_setpos( pmenu, atof(argv[2]), atof(argv[3]) ); } else if(argc == 3 && !strcmp(argv[1], "wall")) { menu_onwall( pmenu, atoi(argv[2]) ); menu_onwall( stubmenu, atoi(argv[2]) ); } else if(argc >= 2 && !strncmp(argv[1], "hid", 3)) { st->hidemenu = argc>2 ? getbool( argv[2], st->hidemenu ) : st->hidemenu ? 0 : -1; msg("fmenu hide %s", st->hidemenu ? "on" : "off"); } else if(argc >= 2 && !strcmp(argv[1], "show")) { st->hidemenu = argc>2 ? getbool( argv[2], st->hidemenu ) : st->hidemenu ? 0 : -1; msg("fmenu show %s", st->hidemenu ? "off" : "on"); } else if(argc >= 2 && !strncmp(argv[1], "h", 1)) { if(argc>2) menu_setheight( pmenu, atof(argv[2]) ); msg("fmenu height %g", pmenu->yspan); } else if(argc >= 2 && !strncmp(argv[1], "demandfps", 6)) { if(argc == 3) sscanf(argv[2], "%f", &st->menudemandfps); msg("fmenu demandfps %g", st->menudemandfps); } else if(argc > 1 && argc <= 3 && !strncmp(argv[1], "font", 4)) { if(argc > 2) { menu_setfont( pmenu, argv[2] ); menu_setfont( stubmenu, argv[2] ); } msg("fmenu font %s", menu_getfont(pmenu)); } else if(argc == 2 && atof(argv[1]) != 0) { menu_setheight( pmenu, atof(argv[1]) ); } else if(argc == 3) { menu_setpos( pmenu, atof(argv[1]), atof(argv[2]) ); } else if(argc <= 1 || argv[1][0] == '?' || !strncmp(argv[1],"help",4)) { msg("\"fmenu ?\" for help"); msg("fmenu pos %.3f %.3f (xpos ypos, in pixels)", pmenu->x0, pmenu->y0); msg("fmenu height %.3f (fraction of screen height; negative = inverted)", pmenu->yspan); msg("fmenu wall %d (cave.h wall number)", pmenu->cavewall); msg("fmenu hide [on|off]"); msg("fmenu font %s (X font name)", menu_getfont(pmenu)); msg("fmenu demandfps %g (hide scene if FPS too low)", st->menudemandfps); } else { msg("fmenu: expected \"pos\" or \"height\" or \"hide\" or \"font\" or \"wall\"."); msg("Try just \"fmenu\" for help."); } #endif } else if(!strcmp( argv[0], "datascale" ) && argc>1) { float s, v = atof(argv[1]); if(v!=0) { struct specklist *sl; struct speck *p; int i; for(sl = st->sl; sl != NULL; sl = sl->next) { s = v / sl->scaledby; for(i = 0, p = sl->specks; i < sl->nspecks; i++, p = NextSpeck(p, sl, 1)) { p->p.x[0] *= s; p->p.x[1] *= s; p->p.x[2] *= s; } sl->scaledby = v; } } } else if(!strcmp( argv[0], "where" ) || !strcmp( argv[0], "w" )) { tellwhere(st); } else return 0; *stp = st; /* in case it changed, e.g. because of an "object" cmd */ return 1; }