static char local_id[] = "$Id$"; /* * Brains of partiview: carrying and displaying data, parsing commands. * Stuart Levy, slevy@ncsa.uiuc.edu * National Center for Supercomputing Applications, * University of Illinois 2001. */ /* * $Log$ * Revision 1.84 2002/04/22 17:58:49 slevy * Allow "w" option to "bound" command: compute bounding box in world * instead of object coordinates. * * Revision 1.83 2002/04/17 20:47:58 slevy * Add a not-quite-proprietary notice to all source files. * Once we pick a license this might change, but * in the mean time, at least the NCSA UIUC origin is noted. * * Revision 1.82 2002/04/16 18:40:26 slevy * Move tokenize() and rejoinargs() into findfile.c, * and out of this overstuffed piece of junk. * * Revision 1.81 2002/04/12 05:50:20 slevy * Fix (?) "only=" etc. commands, broken since I added the selection stuff last July! * * Revision 1.80 2002/03/31 22:40:32 slevy * "tfm" command -- only print full matrices if "tfm -v" given, * otherwise just x y z rx ry rz scale; print matrices in %g format for * full precision; accept 7th parameter as scale factor on input * just as we print it on output. * * Revision 1.79 2002/03/14 18:14:58 slevy * Peel out a bit more CAVE code, * and refer specifically to Marcus Thiebaux's virdir * since there's now also a Matt Hall version. * * Revision 1.78 2002/03/14 04:54:09 slevy * Strip out a bunch of virdir-specific CAVE menu clutter, * now moved into partimenu.c. * * Revision 1.77 2002/03/11 22:31:23 slevy * Move virdir-specific parti_seto2w() and specks_display() to partiview.cc. * "rawdata -a" particle-dump includes position, color, brightness too. * * Revision 1.76 2002/02/02 04:13:07 slevy * Unless PARTIMENU explicitly set, just get rid of the blasted menus. * * Revision 1.75 2002/01/27 00:38:19 slevy * Add "setenv name value" data command. * * Revision 1.74 2002/01/23 03:56:34 slevy * Add "timealign", "skipblanktimes" commands. * Default change: no longer skip blank times! * Per-data time ranges now extend overall clock range, * don't replace it. * Reset material properties (shininess, specular, ambient) * when drawing a lighted mesh. * * Revision 1.73 2001/12/28 07:23:57 slevy * Move plugins (kira, warp, ieeeio) off to separate source files. * "where" command gives full-precision values, and reports jump-pos with scale. * Allow for model-specific render function (initially for Maya models). * env TXDEBUG. * * Revision 1.72 2001/11/09 05:27:53 slevy * In specks_set_timestep(), don't set clock's range if we only have * a single timestep ourselves! * * Revision 1.71 2001/11/08 06:32:52 slevy * Extract async code into async.c. * Add pointsize metering, normally #ifdef USE_PTRACK'ed out. * * Revision 1.70 2001/11/06 06:20:30 slevy * Centralize test for nonempty timestep slot: specks_nonempty_timestep(). * Fix tokenize() for case of "quoted" or 'quoted' arg strings. * * Revision 1.69 2001/10/19 17:48:51 slevy * Don't leak FILE in specks_read_waveobj(). * * Revision 1.68 2001/08/29 17:54:00 slevy * Make rejoinargs() work robustly -- use a private static malloced area, * don't overwrite args in memory. * * Revision 1.67 2001/08/28 18:27:34 slevy * Remove lots of unused variables. Move some used only in CAVE code * inside #ifdef brackets. * * Revision 1.66 2001/08/28 02:05:22 slevy * Fix format string usage. * * Revision 1.65 2001/08/26 17:40:04 slevy * Bounds-check all references to st->meshes[][], st->annot[][], etc. -- * all those maintained by specks_timespecksptr. We no longer guarantee * that st->curtime is always in range 0..st->ntimes-1, so just yield NULL * for out-of-range references. New CURDATATIME(fieldname) does it. * * Revision 1.64 2001/08/26 02:10:40 slevy * "color rgb ..." sets colors of otherwise-uncolored mesh objects. * Only call parti_parse_args() if non-CAVE. * * Revision 1.63 2001/08/24 01:02:38 slevy * Add polygonal meshes -- wavefront .obj models. * * Revision 1.62 2001/08/16 19:58:56 slevy * Pull non-specks-specific code out of partibrains.c * and into partiview.cc, using new parti_add_commands hook. * * Revision 1.61 2001/08/16 17:23:41 slevy * New parti_parse_args() for generic add-on commands. * "where" prints more information. * * Revision 1.60 2001/07/19 20:10:23 slevy * st->threshsel is now kept as a SEL_DEST. * Fix seldest2src(), not even wrong. * Use specks_reupdate() after changing thresh, so the reported * selcount should be right. * specks_rethresh(): keep track of whether anything changed, * and increment st->selseq if so. * * Revision 1.59 2001/07/18 19:24:57 slevy * parse_selexpr() returns bit-encoded value saying whether src or dest or * both were filled in. * * Revision 1.58 2001/07/17 17:28:08 slevy * Increase MAXPTSIZE to allow bigger dots! * * Revision 1.57 2001/07/16 17:58:37 slevy * Don't take spurious snapshot if "snapset" command gets -... args. * Revert to compatible texture behavior: texture -A is again the default, * use texture -O for "over"-style blending. * * Revision 1.56 2001/07/15 23:09:46 slevy * Um, let "wanted == 0" and SEL_USE indicate that we want to match all pcles. * * Revision 1.55 2001/07/15 22:55:14 slevy * Er, those 'leadc' parameters to seldest()/selsrc() are ints, not SelTokens. * * Revision 1.54 2001/07/12 21:42:25 slevy * Use new selcounts() function to report stats whenever we mention a SelOp. * ``sel <src>'' alone now works too, to just report match count. * * Revision 1.53 2001/07/10 17:18:05 slevy * threshsel is a src, not a dest-type SelOp. * * Revision 1.52 2001/07/09 23:46:56 slevy * A bit more ripening of selection system... * And, report match counts from "sel", "thresh", "emph". * * Revision 1.51 2001/07/07 15:35:49 slevy * Implement more (most?) of selection framework: * selsrc(), seldest(), selname(), seltoken(). * "thresh" now accepts "-s 'destexpr'". * Add new selthresh SelOp. * * Revision 1.50 2001/07/04 03:44:12 slevy * Add framework of set-selection stuff; not really usable yet. * Next: implement "see" in drawspecks(). * Add "picked" callback which gets notified of all pick results. * * Revision 1.49 2001/06/30 18:14:22 slevy * Provide for storing another bit in rgba alpha field: EMPHBIT, for * emphasis tag. * Use new sfStrDrawTJ (with transform & justification) for text. * Yeow, handle polymax properly -- default == infinity! * Add "vcmap" for per-datafield colormaps. * Enable GL_SMOOTH mode to get texture colors to work right (huh? why do we need to?). * "tfm c"/"tfm w" choose cam vs. world (default) location for object tfms. * * Revision 1.48 2001/05/30 14:32:55 slevy * Add subcameras ("subcam" command). * popen() needs "r" not "rb" -- no such thing as binary mode. * kira_open() gets original filename if we can't find it, for error messaging. * * Revision 1.47 2001/05/15 12:18:57 slevy * New interface to dynamic-data routines. * Now, the only #ifdef KIRA/WARP needed in partibrains.c are * the data-command initialization routines. All others, * including control-command parsing and specialized drawing, * is now via a function table. * * Revision 1.46 2001/05/14 15:51:34 slevy * Initialize new speckseq value whenever we make a new specklist. * Put colorseq/sizeseq/threshseq invalidation in kira_parti, not here, * since we needn't do it when warping. * By default, clock range is whatever it had been set to before. * * Revision 1.45 2001/05/12 07:22:24 slevy * Add get-time-range func for dynamic data. * Initialize all dynamic-data stuff explicitly. * * Revision 1.44 2001/05/11 10:05:38 slevy * Add "warp" command if -DUSE_WARP. * Ellipsoids allow 3-component (Rx Ry Rz) orientation numbers too. * st->dyndata doesn't mean we should trash the anima[][] specklist. * Need to keep it intact for warping. * * Revision 1.43 2001/05/02 09:51:01 slevy * Add "ellipsoids" and "meshes" commands to toggle their display. * Finally parse snapshot arguments properly...? * * Revision 1.42 2001/04/26 08:52:46 slevy * Add hook for off-screen rendering: "-w width[xheight]" option for * snapshot/snapset. Not actually implemented yet. * Add "clipbox hide". This (a) doesn't display the big yellow box * and (b) doesn't enable OpenGL clipping -- just whatever object culling * the draw routine does anyway. * specks_draw_mesh() now pays attention to drawing style (solid/line/plane/point). * When drawing ellipsoids, disable blending altogether if bgcolor != 0. * Small, distant labels now disappear by default. To get the old * draw-tiny-line behavior, use a negative "labelmin" value. * * Revision 1.41 2001/04/20 13:47:39 slevy * Allow *cment commands to alter multiple colormap entries: new editcmap(). * * Revision 1.40 2001/04/10 19:18:33 slevy * Open sdb files (and while we're at it, other files too) in binary mode. * * Revision 1.39 2001/04/04 20:33:02 slevy * Use findfile() for "read" ctrl-command. * * Revision 1.38 2001/03/30 16:49:00 slevy * Change enum SurfStyle to avoid mentioning POINT, which Windows uses too. * * Revision 1.37 2001/03/30 14:00:42 slevy * Allow cment/boxcment/textcment as data commands too. * Add "ghosts" ctl command (not really implemented). * Add "winsize" ctl command. * Ellipsoids now accept -l levelno option, in which case * "hide levelno"/"show levelno" applies to them. Default is -1, * which means "show if any level is shown", but hidden by "hide all". * Scrap ghost maintenance. Do that elsewhere instead. * Make "maxcomment" work properly. * * Revision 1.36 2001/03/19 11:55:08 slevy * New version.c (derived from ../VERSION) contains current version string. * New "version" command in partibrains reports that and the partibrains.c CVS ver no. * * Revision 1.35 2001/03/19 10:39:07 slevy * Handle speck comments properly. * * Revision 1.34 2001/03/15 18:19:06 slevy * Don't include comments in argc/argv -- it complicates things. * Have a separate "comment" pointer. Pass it to specks_read_ellipsoid too. * * Revision 1.33 2001/03/15 15:37:06 slevy * Yeow -- handle VIRDIR prefix properly! * Complain of unrecognized data commands. * * Revision 1.32 2001/03/14 17:27:05 slevy * Make rejoinargs() work -- don't omit last arg. * "object" command alone reports our currently selected object. * * Revision 1.31 2001/03/13 22:45:08 slevy * Add "verbose" flag to parti_allobjs, so "gall -v ..." * reports object name before invoking each command. * * Revision 1.30 2001/03/13 08:23:39 slevy * Use parti_object's new "create-if-not-present" flag. * Data-language references can create, command-language ones can't. * Switch specks_read() to argc/argv style, using new "tokenize()" function. * Some commands still want the original string, so we also have rejoinargs(). * Allow for adjustable comment length with "maxcomment" data command. * New "ellipsoid" and "mesh" objects. Only quadmeshes implemented right now. * Data-language "tfm" command is now quiet. Command-language tfm still verbose. * Check at run time for endian-config errors and refuse to run if wrong! * * Revision 1.29 2001/03/08 22:02:29 slevy * Disable the parti menu unless PARTIMENU envar set. * Allow alpha to adjust brightness of mesh objects. * * Revision 1.28 2001/03/05 03:04:41 slevy * Add mesh objects. Or, quad meshes, anyway. * * Revision 1.27 2001/03/04 16:47:35 slevy * Make "add" and "eval" behave consistently. * polyorivar and texturevar now accept field names as well as numbers. * * Revision 1.26 2001/02/22 20:04:34 slevy * CONSTify. * * Revision 1.25 2001/02/19 22:01:16 slevy * Satisfy windows C compiler: pull enum FadeModel outside struct speck, etc. * * 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 RGBA_R(rgba) (((rgba)>>24) & 0xFF) #define RGBA_G(rgba) (((rgba)>>16) & 0xFF) #define RGBA_B(rgba) (((rgba)>>8) & 0xFF) #define RGBA_A(rgba) (((rgba)) & 0xFF) #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 RGBA_R(rgba) (((rgba)) & 0xFF) #define RGBA_G(rgba) (((rgba)>>8) & 0xFF) #define RGBA_B(rgba) (((rgba)>>16) & 0xFF) #define RGBA_A(rgba) (((rgba)>>24) & 0xFF) #endif #define THRESHBIT PACKRGBA(0,0,0,1) #define EMPHBIT PACKRGBA(0,0,0,2) #define EXTRABITS PACKRGBA(0,0,0,0xff) #define RGBBITS PACKRGBA(0xff,0xff,0xff,0) #include "geometry.h" #include <GL/gl.h> #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 #if CAVEMENU # include "cavemenu.h" # include "partimenu.h" # define IFCAVEMENU(x) (x) #else # define IFCAVEMENU(x) /* nothing */ #endif #if THIEBAUX_VIRDIR # include "vd_util.h" #endif /* Star Renderer (.sdb) structure -- from stardef.h */ typedef enum {ST_POINT, ST_BRIGHT_CLOUD ,ST_DARK_CLOUD, ST_BOTH_CLOUD, ST_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] ) 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 ); extern int getfloats( float *v, int nfloats, int arg0, int argc, char **argv ); extern int specks_set_byvariable( struct stuff *st, char *str, int *val ); 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 ); i = PACKRGBA(1, 0, 0, 0); if(*(char *)&i != 1) { msg("specks_init(): trouble: is WORDS_BIGENDIAN mis-set? Giving up."); exit(1); } 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->usemeshes = 1; st->staticmeshes = NULL; st->mullions = 0; st->useellipsoids = 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->maxcomment = sizeof(st->sl->specks->title) - 1; st->dyn.enabled = 0; st->dyn.data = NULL; st->dyn.getspecks = NULL; st->dyn.draw = NULL; st->dyn.trange = NULL; st->dyn.help = NULL; st->dyn.ctlcmd = NULL; st->dyn.free = NULL; st->speckseq = 0; 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 = 1e8; /* don't allow polygons to get bigger than this (pixels) */ st->polyfademax = 0; 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->useemph = 0; selinit( &st->emphsel ); st->emphfactor = 10; selinit( &st->threshsel ); selinit( &st->seesel ); 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)); memset(st->meshes, 0, sizeof(st->meshes)); #if CAVE shmrecycler( specks_purge, st ); #endif for(i = 1; i < argc; i++) specks_read( &st, argv[i] ); IFCAVEMENU( partimenu_init( st ) ); return st; } void specks_rethresh( struct stuff *st, struct specklist *sl, int by ) { int i; int curdata = st->curdata; int nel = sl->nspecks; float threshmin = st->thresh[0], threshmax = st->thresh[1]; struct speck *p = sl->specks; SelMask *sel = sl->sel; SelOp threshseldest; int changed = 0; int min, max; 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; threshseldest = st->threshsel; for(i = 0, p = sl->specks; i < nel; i++, p = NextSpeck( p, sl, 1 )) { SelMask was = sel[i]; if((min&&p->val[by]<threshmin) || (max&&p->val[by]>threshmax)) { /* p->rgba |= THRESHBIT; */ SELUNSET( sel[i], &threshseldest ); } else { /* p->rgba &= ~THRESHBIT; */ SELSET( sel[i], &threshseldest ); } changed |= was ^ sel[i]; } if(changed) { sl->selseq++; if(st->selseq < sl->selseq) st->selseq = sl->selseq; } } 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; struct cment *cmap = st->cmap; 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 & EXTRABITS); } return; } if(by >= MAXVAL || by < 0 || SMALLSPECKSIZE(by) > sl->bytesperspeck) by = 0; vd = &st->vdesc[curdata][by]; if(vd->vncmap > 0) { ncmap = vd->vncmap; cmap = vd->vcmap; } 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 & EXTRABITS); } 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 = cmap[index].cooked | (sp->rgba & EXTRABITS); } } } 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; if(SELECTED(sl->sel[i], &st->emphsel)) { sp->size *= st->emphfactor; } } 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); if(st->useemph && st->emphsel.use != SEL_NONE) { for(i = 0; i < nel; i++, sp = NextSpeck(sp, sl, 1)) { sp->size = (sp->val[by] - lmin) * normal; if(SELECTED(sl->sel[i], &st->emphsel)) { sp->size *= st->emphfactor; } } } else { /* normal case */ 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_timebase( struct stuff *st, double timebase ) { clock_set_timebase( st->clk, timebase ); /* parti_set_timebase( st, timebase ); */ } 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_set_timestep( struct stuff *st ) { struct specklist *sl = st->sl; struct specklist **slp; double realtime, tmin, tmax; int timestep; st->used++; tmin = st->clk->tmin; tmax = st->clk->tmax; specks_timerange( st, &tmin, &tmax ); if(st->usertrange) { /*if(tmin < st->utmin)*/ tmin = st->utmin; /*if(tmax > st->utmax)*/ tmax = st->utmax; } /* Only expand clock's existing time range */ if(tmin > st->clk->tmin) tmin = st->clk->tmin; if(tmax < st->clk->tmax) tmax = st->clk->tmax; clock_set_range( st->clk, tmin, tmax, st->utwrap ); realtime = clock_time( st->clk ); timestep = realtime; if(st->dyn.enabled && st->dyn.getspecks) { struct specklist *sl; if(realtime == st->currealtime && st->sl != NULL && st->sl->used >= 0) return; sl = (*st->dyn.getspecks)(&st->dyn, st, realtime); if(sl == NULL) return; sl->used = 1; st->currealtime = realtime; st->curtime = timestep; slp = specks_timespecksptr( st, 0, 0 ); st->sl = sl; /* Do we need this? */ /* *slp = sl; */ /* Guess not... */ parti_set_timestep( st, realtime ); 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(st->skipblanktimes && sl == NULL && !(st->boxtimes>timestep && st->boxes[timestep]!=NULL) && st->meshes[st->curdata][timestep] == NULL) return; st->sl = sl; /* st->sl <= anima[][] */ st->curtime = timestep; st->currealtime = timestep; #if !THIEBAUX_VIRDIR st->frame_time = st->curtime; /* if non-VD, we have no frame-function */ #endif { struct specklist *asl = CURDATATIME(annot); specks_set_current_annotation( st, asl ? asl->text : NULL ); } parti_set_timestep( st, timestep ); } void specks_set_current_annotation( struct stuff *st, char *annotation ) { st->annotation = annotation; IFCAVEMENU( partimenu_annot( st->annotation ) ); } 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]; /* safe -- no need for CURDATATIME() check */ if((sl = *slp) == NULL) { sl = *slp = NewN( struct specklist, 1 ); memset(*slp, 0, sizeof(**slp)); sl->speckseq = ++st->speckseq; } 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, "rb"); 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); 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->speckseq = ++st->speckseq; sl->bytesperspeck = SMALLSPECKSIZE( nvars ); if(nvars > MAXVAL) nvars = MAXVAL; sl->scaledby = st->spacescale; sp = NewNSpeck(sl, nspecks); sl->specks = sp; sl->sel = NewN( SelMask, nspecks ); sl->nsel = nspecks; memset(sl->sel, 0, nspecks*sizeof(SelMask)); 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->dyn.enabled && st->dyn.trange && (*st->dyn.trange)(&st->dyn, st, tminp, tmaxp)) { /* OK */ } else { *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; } /* 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; struct mesh **nmesh; 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 ); nmesh = NewN( struct mesh *, 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)); memset(nmesh, 0, needroom * sizeof(*nmesh)); 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) ); if(d < st->ndata && st->meshes[d]) memcpy( nmesh, st->meshes[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->meshes[d] = nmesh; } 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 ); } } static int specks_nonempty_timestep( struct stuff *st, int timestep ) { if(specks_timespecks( st, st->curdata, timestep ) != NULL) return 1; if((st->datafile[st->curdata] != NULL && st->datafile[st->curdata][timestep] != NULL)) return 1; if(timestep >= 0 && timestep < st->ntimes && st->meshes[st->curdata][timestep] != NULL) return 1; if(timestep >= 0 && timestep < st->boxtimes && st->boxes[timestep] != NULL) return 1; return 0; } void specks_set_time( struct stuff *st, double newtime ) { 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; if(st->skipblanktimes) { for(nudges = 0; nudges < st->ntimes; nudges++) { ts = (st->curtime + nudges) % st->ntimes; if( specks_nonempty_timestep( st, ts ) ) break; } } clock_set_time( st->clk, ts ); specks_set_timestep( st ); } /* st->playnext = now + (st->fspeed != 0 ? 1/st->fspeed : 0); */ IFCAVEMENU( partimenu_setpeak( st ) ); specks_reupdate( st, st->sl ); IFCAVEMENU( partimenu_refresh( st ) ); } 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 ); extern void specks_draw_mesh( struct stuff *st, struct mesh *m, int *texturing ); extern void specks_draw_ellipsoid( struct stuff *st, struct ellipsoid *e ); 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; 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 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]; 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 & RGBBITS; 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 fast = st->fast; int inpick = st->inpick; /*int usethresh = st->usethresh&P_USETHRESH ? THRESHBIT : 0;*/ #if USE_PTRACK int useptrack = (getenv("PTRACK") != NULL); #endif SelOp seesel = st->seesel; float polyminrad, polymaxrad; 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; enum FadeType 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(st->dyn.enabled && st->dyn.draw) (*st->dyn.draw)( &st->dyn, st, slhead, &Tc2w, radperpix ); skip = st->subsample; if(slhead && 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; static int txdebug = -1; if(txdebug == -1) { if(getenv("TXDEBUG")) txdebug = atoi(getenv("TXDEBUG")); else txdebug = 0; } if(!(txdebug&1)) { glMatrixMode( GL_TEXTURE ); glLoadIdentity(); glTranslatef( .5, .5, 0 ); glScalef( st->txscale, st->txscale, st->txscale ); } glMatrixMode( GL_MODELVIEW ); /* Textures seem to be multiplied by some mysterious factor, * unless we're in GL_SMOOTH mode. What gives? */ if(!(txdebug&2)) glShadeModel( texturevar >= 0 ? GL_SMOOTH : GL_FLAT ); 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 { int usepolymax = (st->polymax < 1e8); #ifdef POLYFADE int polyfade = 0; if(st->polyfademax > st->polymin) { polyfade = 1; polyfadesize = glEnable(GL_BLEND); glBlendFunc( GL_SRC_ALPHA, additive_blend ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA ); } #endif 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(!SELECTED(sl->sel[i], &seesel)) continue; size = p->val[sizevar] * polysize; if(dist + size <= 0) continue; if(usearea) { if(size < dist * dist * mins2d) continue; size = sqrtf(size); } else { if(size < dist * polyminrad) continue; } if(usepolymax && size > dist * polymaxrad && dist > 0) size = dist * polymaxrad; rgba = p->rgba & RGBBITS; 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 32 /* 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(!SELECTED(sl->sel[i], &seesel)) 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(!SELECTED(sl->sel[i], &seesel)) 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; } #ifdef USE_PTRACK if(useptrack) printf("pfast %d %d %d %d\n", lum, pxsize, myalpha, invgamma[myalpha] & 0xFC); #endif 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&RGBBITS, 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(!SELECTED(sl->sel[i], &seesel)) 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; } #ifdef USE_PTRACK if(useptrack) printf("paa %d %d/2 %d %d\n", lum, pxsize, myalpha, invgamma[myalpha] & 0xFC); #endif if(pxsize < 1 || pxsize >= MAXPTSIZE*2 || myalpha <= 0 || myalpha > 255) { static int oops; oops++; pxsize = MAXPTSIZE*2 - 1; myalpha = 255; } rgba = RGBALPHA( p->rgba&RGBBITS, 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; int textmin = abs(st->textmin); int label_stubs = (st->textmin < 0); 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 * textmin)) { if(label_stubs) { /* Draw a stub: 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, label is big enough to see -- draw actual text */ if(st->usetextaxes) { static unsigned char col[3][3] = {255,0,0, 0,255,0, 0,0,255}; glBegin(GL_LINES); for(k = 0; k < 3; k++) { glColor3ubv( &col[k][0] ); glVertex3fv( p->p.x ); glVertex3f( p->p.x[0] + (k==0?tsize:0), p->p.x[1] + (k==1?tsize:0), p->p.x[2] + (k==2?tsize:0) ); } glEnd(); } 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; } sfStrDrawTJ( sl->text, p->size, &p->p, &Ttext, NULL ); } } } if(inpick) glLoadName(0); if(st->usemeshes) { struct mesh *m; int texturing = 0; if(additive_blend && st->alpha < 1) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE ); } else { glDisable( GL_BLEND ); } glColor4f(1, 1, 1, st->alpha); for(m = st->staticmeshes; m != NULL; m = m->next) specks_draw_mesh(st, m, &texturing); for(m = CURDATATIME(meshes); m != NULL; m = m->next) specks_draw_mesh(st, m, &texturing); if(texturing) txbind(NULL, NULL); } if(st->useellipsoids) { struct ellipsoid *e; if(additive_blend) { glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE ); } else { glDisable( GL_BLEND ); } glColor4f(1, 1, 1, st->alpha); for(e = st->staticellipsoids; e != NULL; e = e->next) specks_draw_ellipsoid(st, e); } 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_ellipsoid( struct stuff *st, struct ellipsoid *e ) { int nu = e->nu; int nv = e->nv; int u, v; float *su, *cu, *sv, *cv; if(st->boxlevelmask == 0 || e->level >= 0 && ((1 << e->level) & st->boxlevelmask) == 0) return; if(nu == 0 && nv == 0) { nu = 15; nv = 10; } su = NewA( float, nu ); cu = NewA( float, nu ); sv = NewA( float, nv ); cv = NewA( float, nv ); for(u = 0; u < nu; u++) { float tu = 2*M_PI*u/nu; su[u] = sin(tu); cu[u] = cos(tu); } for(v = 0; v < nv; v++) { float tv = M_PI*v/(nv-1); sv[v] = sin(tv); cv[v] = cos(tv); } glPushMatrix(); glTranslatef( e->pos.x[0],e->pos.x[1],e->pos.x[2] ); if(e->hasori) glMultMatrixf( &e->ori.m[0] ); glScalef( e->size.x[0], e->size.x[1], e->size.x[2] ); if(e->cindex >= 0) { int rgba = RGBALPHA( RGBWHITE & st->cmap[(e->cindex < st->ncmap) ? e->cindex : st->ncmap-1].cooked, (int)(st->alpha * 255)); glColor4ubv((GLubyte *)&rgba); } switch(e->style) { case S_SOLID: for(v = 1; v < nv; v++) { glBegin( GL_QUAD_STRIP ); for(u = 0; u < nu; u++) { glVertex3f( cu[u]*sv[v-1], su[u]*sv[v-1], cv[v-1] ); glVertex3f( cu[u]*sv[v], su[u]*sv[v], cv[v] ); } glVertex3f( cu[0]*sv[v-1], su[0]*sv[v-1], cv[v-1] ); glVertex3f( cu[0]*sv[v], su[0]*sv[v], cv[v] ); glEnd(); } break; case S_LINE: for(v = 1; v < nv-1; v++) { glBegin( GL_LINE_LOOP ); for(u = 0; u < nu; u++) glVertex3f( cu[u]*sv[v], su[u]*sv[v], cv[v] ); glEnd(); } for(u = 0; u < nu; u++) { glBegin( GL_LINE_STRIP ); for(v = 0; v < nv; v++) glVertex3f( cu[u]*sv[v], su[u]*sv[v], cv[v] ); glEnd(); } break; case S_PLANE: glBegin( GL_LINE_LOOP ); for(u = 0; u < nu; u++) glVertex3f( cu[u], su[u], 0 ); glEnd(); glBegin( GL_LINE_LOOP ); for(u = 0; u < nu; u++) glVertex3f( cu[u], 0, su[u] ); glEnd(); glBegin( GL_LINE_LOOP ); for(u = 0; u < nu; u++) glVertex3f( 0, cu[u], su[u] ); glEnd(); break; case S_POINT: glPointSize( 1.0 ); glBegin( GL_POINTS ); glVertex3f( 0,0,1 ); glVertex3f( 0,0,-1 ); for(v = 1; v < nv-1; v++) { for(u = 0; u < nu; u++) glVertex3f( cu[u]*sv[v], su[u]*sv[v], cv[v] ); } glEnd(); break; } glPopMatrix(); } void specks_draw_mesh( struct stuff *st, register struct mesh *m, int *texturing ) { int u, v, prev, cur; int usetx; float gap, ungap; int f, was; int usemullions = (st->mullions > 0); usetx = (st->usetextures && m->tx != NULL && m->txno >= 0 && m->txno < st->ntextures && st->textures[m->txno] != NULL); txbind( usetx ? st->textures[m->txno] : NULL, texturing ); if(m->fnorms != NULL) { static float amb[3] = { 0,0,0 }; static float spec[3] = { .4,.4,.4 }; glEnable( GL_LIGHTING ); glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); glEnable( GL_COLOR_MATERIAL ); glShadeModel( GL_SMOOTH ); glMaterialf( GL_FRONT_AND_BACK, GL_SHININESS, 20. ); glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, amb ); glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, spec ); } if(m->cindex >= 0) { int rgba = RGBALPHA( RGBWHITE & st->cmap[(m->cindex < st->ncmap) ? m->cindex : st->ncmap-1].cooked, (int)(st->alpha * 255)); glColor4ubv((GLubyte *)&rgba); } else if(st->coloredby == CONSTVAL) { struct valdesc *vd = &st->vdesc[st->curdata][CONSTVAL]; glColor4f( vd->cmin, vd->cmax, vd->mean, st->alpha ); } switch(m->type) { case MODEL: if(m->objrender) (*m->objrender)( st, m ); break; case QUADMESH: switch(m->style) { case S_SOLID: for(v = 1; v < m->nv; v++) { prev = (v-1) * m->nu; cur = v * m->nu; glBegin( GL_QUAD_STRIP ); for(u = 0; u < m->nu; u++) { if(usetx) { glTexCoord2fv( &m->tx[prev+u].x[0] ); glVertex3fv( &m->pts[prev+u].x[0] ); glTexCoord2fv( &m->tx[cur+u].x[0] ); glVertex3fv( &m->pts[cur+u].x[0] ); } else { glVertex3fv( &m->pts[prev+u].x[0] ); glVertex3fv( &m->pts[cur+u].x[0] ); } } glEnd(); } break; case S_LINE: for(v = 0; v < m->nv; v++) { glBegin( GL_LINE_STRIP ); cur = v * m->nu; for(u = 0; u < m->nu; u++) glVertex3fv( &m->pts[cur+u].x[0] ); glEnd(); } for(u = 0; u < m->nu; u++) { glBegin( GL_LINE_STRIP ); for(v = 0; v < m->nv; v++) glVertex3fv( &m->pts[v*m->nu + u].x[0] ); glEnd(); } break; case S_POINT: cur = m->nu * m->nv; glBegin( GL_POINTS ); for(u = 0; u < cur; u++) glVertex3fv( &m->pts[u].x[0] ); glEnd(); break; case S_PLANE: glBegin( GL_LINE_LOOP ); for(u = 0; u < m->nu; u++) glVertex3fv( &m->pts[u].x[0] ); for(v = 1; v < m->nv; v++) glVertex3fv( &m->pts[v*m->nu + m->nu-1].x[0] ); cur = (m->nv-1) * m->nu; for(u = m->nu; --u >= 0; ) glVertex3fv( &m->pts[cur+u].x[0] ); for(v = m->nv-1; --v > 0; ) glVertex3fv( &m->pts[v*m->nu].x[0] ); glEnd(); break; } break; case POLYMESH: gap = st->mullions; ungap = 1 - gap; was = 0; for(f = 0; f < m->nfaces; f++) { int fv0 = m->fv0[f]; int fvn = m->fvn[f]; int *fvs0 = &m->fvs[ fv0 ]; Point *pts = m->pts; Point bary, *pa, *pb, *pc; if(usemullions && fvn == 3) { pa = &pts[ fvs0[0] ]; pb = &pts[ fvs0[1] ]; pc = &pts[ fvs0[2] ]; bary.x[0] = (pa->x[0] + pb->x[0] + pc->x[0]) * 0.3333333333; bary.x[1] = (pa->x[1] + pb->x[1] + pc->x[1]) * 0.3333333333; bary.x[2] = (pa->x[2] + pb->x[2] + pc->x[2]) * 0.3333333333; #define INTERP(p) p->x[0]*gap + bary.x[0]*ungap, p->x[1]*gap + bary.x[1]*ungap, p->x[2]*gap + bary.x[2]*ungap if(was) { was = 0; glEnd(); } glBegin( GL_QUAD_STRIP ); glNormal3fv( m->fnorms[f].x ); glVertex3fv(pa->x); glVertex3f( INTERP(pa) ); glVertex3fv(pb->x); glVertex3f( INTERP(pb) ); glVertex3fv(pc->x); glVertex3f( INTERP(pc) ); glVertex3fv(pa->x); glVertex3f( INTERP(pa) ); glEnd(); } else if(fvn == 3) { if(was != GL_TRIANGLES) { if(was) glEnd(); glBegin( was=GL_TRIANGLES ); } glNormal3fv( m->fnorms[f].x ); glVertex3fv( pts[ fvs0[0] ].x ); glVertex3fv( pts[ fvs0[1] ].x ); glVertex3fv( pts[ fvs0[2] ].x ); } else if(fvn == 4) { if(was != GL_QUADS) { if(was) glEnd(); glBegin( was=GL_QUADS ); } glNormal3fv( m->fnorms[f].x ); glVertex3fv( pts[ fvs0[0] ].x ); glVertex3fv( pts[ fvs0[1] ].x ); glVertex3fv( pts[ fvs0[2] ].x ); glVertex3fv( pts[ fvs0[3] ].x ); } else if(fvn < 0) { if(was) { glEnd(); was = 0; } glBegin( GL_TRIANGLE_STRIP ); glNormal3fv( m->fnorms[f].x ); do { glVertex3fv( pts[ *fvs0++ ].x ); } while(++fvn < 0); glEnd(); } else { if(was) { glEnd(); was = 0; } glBegin( GL_POLYGON ); glNormal3fv( m->fnorms[f].x ); do { glVertex3fv( pts[ *fvs0++ ].x ); } while(--fvn > 0); glEnd(); } } if(was) glEnd(); break; } if(m->fnorms != NULL) { glDisable( GL_LIGHTING ); glDisable( GL_COLOR_MATERIAL ); } } 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; 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; Point mid; char lbl[16]; vlerp( &mid, .5, &box->p0, &box->p1 ); sprintf(lbl, "%d", box->boxno); sfStrDrawTJ( lbl, sz, &mid, &Ttext, "c" ); } } } 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; if(st->picked != NULL) (*st->picked)(st, NULL, NULL, nhits); /* "prepare for up to nhits calls" */ 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(hi = 0, i = 0; hi < nhits && i < nents; hi++, i += ns + 3) { ns = hitbuf[i]; if(ns < 1 || ns > 16) break; /* trouble */ z0 = hitbuf[i+1]; if(id != hitbuf[i+3] || ns <= 1) continue; if(st->usepoly>1) /* debug */ printf(ns>1?"[%x %d/%d]":"[%x %d]", z0, hitbuf[i+3],hitbuf[i+4]); if(bestz > z0 && ns > 1 && (slno = hitbuf[i+4]) > 0) { bestz = z0; bestslno = slno; bestspeck = (ns>2) ? hitbuf[i+5] : 0; } if(st->picked != NULL && ns > 2) { for(sl = slhead, slno = 1; sl != NULL && slno < hitbuf[i+4]; sl = sl->next, slno++) ; if(sl && hitbuf[i+5] < sl->nspecks) if((*st->picked)(st, &hitbuf[i], sl, hitbuf[i+5])) return 0; } } if(st->picked != NULL) { if((*st->picked)(st, NULL, NULL, 0)) return 0; } if(bestslno <= 0) return 0; for(sl = slhead, slno = 1; sl != NULL; sl = sl->next, slno++) { if(slno == bestslno) { if(bestspeck < 0 || bestspeck >= sl->nspecks) { msg("Bogus pick result: sl %x bestspeck %d of 0..%d!\n", sl, bestspeck, sl->nspecks-1); return 0; } if(slp) *slp = sl; if(specknop) *specknop = bestspeck; if(pos) { if(bestspeck < 0 || bestspeck >= sl->nspecks) bestspeck = 0; *pos = NextSpeck( sl->specks, sl, bestspeck )->p; } *bestzp = bestz; return 1; } } return 0; } int selname( struct stuff *st, char *name, int create ) { int i, avail; if(name == NULL) return 0; if(!strcmp(name, "all") || !strcmp(name, "on")) return SEL_ALL; if(!strcmp(name, "none") || !strcmp(name, "off")) return SEL_OFF; if(!strcmp(name, "thresh")) return SEL_THRESH; if(!strcmp(name, "pick")) return SEL_PICK; for(i = COUNT(st->selitems); --i >= 0; ) { if(!strcmp(name, st->selitems[i].name)) return i+1; if(st->selitems[i].name[0] == '\0') avail = i; } if(create) { sprintf(st->selitems[avail].name, "%.15s", name); return avail+1; } return 0; } void selinit( SelOp *sp ) { sp->wanted = 0; sp->wanton = 0; sp->use = SEL_NONE; } static char *selcat( struct stuff *st, char *from, int selno, int preop ) { char *what = ""; static char s[8]; switch(selno) { case SEL_NONE: what = "off"; break; case SEL_ALL: what = "all"; break; case SEL_THRESH: what = "thresh"; break; case SEL_PICK: what = "pick"; break; default: if(selno > 0 && selno <= MAXSEL && st->selitems[selno-1].name[0] != '\0') what = &st->selitems[selno-1].name[0]; else { sprintf(s, "%d", selno<0||selno>MAXSEL?0:selno); what = s; } } if(preop) *from++ = preop; return from + sprintf(from, "%.15s ", what); } char *show_selexpr( struct stuff *st, SelOp *destp, SelOp *srcp ) { char str[(MAXSELNAME + 2)*33]; char *tail = str; static char *sstr; static int sroom = -1; int i; if(destp) { if(destp->wanted == 0) { tail = selcat(st, tail, SEL_ALL, 0 ); } else { for(i = SEL_THRESH; i > 0; i--) { if(~destp->wanted & SELMASK(i)) { tail = selcat( st, tail, i, destp->wanton & SELMASK(i) ? 0 : '-' ); } else if(destp->wanton & SELMASK(i)) { tail = selcat( st, tail, i, '^' ); } } } if(srcp) tail += sprintf(tail, "= "); } if(srcp) { if(srcp->use == SEL_NONE) { tail = selcat( st, tail, srcp->use, 0 ); } else if(srcp->use == SEL_USE && srcp->wanted == 0) { tail = selcat( st, tail, SEL_ALL, 0 ); } else { for(i = SEL_THRESH; i > 0; i--) { if(srcp->wanted & SELMASK(i)) { tail = selcat( st, tail, i, srcp->wanton & SELMASK(i) ? 0 : '-' ); } else if(srcp->wanton & SELMASK(i)) { tail = selcat( st, tail, i, '^' ); } } } } if(tail - str > sroom) { if(sstr) Free(sstr); sroom = tail - str + 60; sstr = NewN( char, sroom ); } if(tail == str) strcpy(sstr, "null"); else { tail[-1] = '\0'; strcpy(sstr, str); } return sstr; } enum SelToken { SEL_ERR, /* unknown */ SEL_EOF, SEL_WORD, /* bare word or +word */ SEL_EQUALS /* = */ }; static enum SelToken seltoken( char **sp, int *leadcp, char word[MAXSELNAME] ) { char *p = *sp; int k; *leadcp = '\0'; word[0] = '\0'; while(isspace(*p)) p++; if(*p == '\0') return SEL_EOF; if(*p == '-' || *p == '^' || *p == '+') *leadcp = *p++; if(*p == '=') { *sp = p+1; return SEL_EQUALS; } if(!(isalnum(*p) || *p == '_')) return SEL_ERR; word[0] = *p++; for(k = 1; isalnum(*p) || *p == '_'; p++) { if(k < MAXSELNAME-1) word[k++] = *p; } word[k] = '\0'; *sp = p; return SEL_WORD; } void seldest2src( struct stuff *st, CONST SelOp *dest, SelOp *src ) { selinit( src ); if(dest->use == SEL_USE) { *src = *dest; } else if(dest->use == SEL_DEST) { src->wanted = ~dest->wanted | dest->wanton; src->wanton = dest->wanton; src->use = SEL_USE; } } void selsrc2dest( struct stuff *st, CONST SelOp *src, SelOp *dest ) { *dest = *src; if(src->use == SEL_USE) { dest->wanted = ~src->wanted; dest->wanton = src->wanton & src->wanted; dest->use = SEL_DEST; } } void seldestinvert( struct stuff *st, SelOp *dest ) { dest->wanton ^= ~dest->wanted; } int seldest( struct stuff *st, SelOp *dest, int selno, int selch ) { SelMask mask; if(dest == NULL) return 0; if(selno == SEL_ALL) { mask = ~0; } else if(selno <= 0 || selno > 32) { return 0; } else { mask = SELMASK(selno); } if(dest->use != SEL_DEST) { dest->use = SEL_DEST; dest->wanted = ~0; dest->wanton = 0; } switch(selch) { case '^': dest->wanted |= mask; dest->wanton |= mask; break; case '-': dest->wanted &= ~mask; dest->wanton &= ~mask; break; case '+': case '\0': dest->wanted &= ~mask; dest->wanton |= mask; break; default: return 0; } return 1; } int selsrc( struct stuff *st, SelOp *src, int selno, int selch ) { SelMask mask; if(src == NULL) return 0; if(src->use == SEL_DEST) { src->wanted = 0; src->wanton = 0; src->use = SEL_USE; } if(selno == SEL_OFF) { src->wanted = src->wanton = mask = ~0; src->use = SEL_NONE; } else if(selno == SEL_ALL) { src->wanted = src->wanton = mask = 0; src->use = SEL_USE; } else if(selno > 0 && selno <= 32) { mask = SELMASK(selno); src->use = SEL_USE; } else { return 0; } switch(selch) { case '-': src->wanted |= mask; src->wanton &= ~mask; break; case '+': case '\0': src->wanted |= mask; src->wanton |= mask; break; default: return 0; } return 1; } /* * [word [op]=] { [op]word }* */ int parse_selexpr( struct stuff *st, char *str, SelOp *destp, SelOp *srcp, char *plaint ) { SelOp dest, src; char wd[MAXSELNAME], w[MAXSELNAME]; char *p = str; int leadc, leadc2; enum SelToken tok, tok2; int ok = 1; int anysrc = 0, anydest = 0; selinit(&dest); selinit(&src); tok = seltoken( &p, &leadc, wd ); if(tok == SEL_EOF) return 0; if(destp) { /* Had better begin with a word, and with no prefix */ int destno; if(tok != SEL_WORD) goto fail; destno = selname( st, wd, 1 ); tok2 = seltoken( &p, &leadc2, w ); if(tok2 == SEL_ERR) goto fail; ok = seldest( st, &dest, destno, leadc ? leadc : leadc2 ); anydest = 1; if(!ok) goto fail; if(tok2 == SEL_WORD) { tok = SEL_WORD; leadc = leadc2; strcpy(wd, w); } else { tok = seltoken( &p, &leadc, wd ); } } while(srcp && tok == SEL_WORD) { ok = selsrc( st, &src, selname( st, wd, 0 ), leadc ); if(!ok) goto fail; if(src.use == SEL_NONE && src.wanted != 0 && destp) { seldestinvert( st, &dest ); selsrc( st, &src, SEL_ALL, 0 ); /* X = off <=> -X = on */ } anysrc = 1; tok = seltoken( &p, &leadc, wd ); } if(tok != SEL_EOF) goto fail; if(destp && anydest) *destp = dest; if(srcp && anysrc) *srcp = src; return (anydest?1:0) + (anysrc?2:0); fail: if(plaint) msg("%s: um, %s", plaint, str); return 0; /*XXX*/ } CONST char *selcounts( struct stuff *st, struct specklist *sl, SelOp *selp ) { int i; static char counts[24]; int yes, total; SelOp selop; if(selp == NULL) return ""; if(selp->use == SEL_DEST) seldest2src( st, selp, &selop ); else selop = *selp; for(yes = total = 0; sl != NULL; sl = sl->next) { SelMask *sel = sl->sel; if(sl->text != NULL || sl->nsel < sl->nspecks || sl->special != SPECKS) continue; total += sl->nspecks; for(i = 0; i < sl->nspecks; i++) if(SELECTED(sel[i], &selop)) yes++; } if(selop.use == SEL_NONE) yes = 0; sprintf(counts, "%d of %d", yes, total); return counts; } SpecksPickFunc specks_all_picks( struct stuff *st, SpecksPickFunc func, void *arg ) { SpecksPickFunc was = st->picked; st->picked = func; st->pickinfo = arg; return was; } static void addchunk( struct stuff *st, int nsp, int bytesperspeck, float scaledby, struct speck *sp, char *text, int outbytesperspeck ) { struct specklist **slp, *sl = NewN( struct specklist, 1 ); memset( sl, 0, sizeof(*sl) ); sl->speckseq = ++st->speckseq; sl->bytesperspeck = outbytesperspeck; sl->specks = NewNSpeck( sl, nsp ); sl->nspecks = nsp; sl->scaledby = scaledby; if(text) { sl->text = NewN( char, strlen(text)+1 ); strcpy(sl->text, text); } else { sl->text = NULL; } if(bytesperspeck == outbytesperspeck) { memcpy( sl->specks, sp, nsp*sl->bytesperspeck ); } else { int i; for(i = 0; i < nsp; i++) memcpy(((char *)sl->specks) + i*outbytesperspeck, ((char *)sp) + i*bytesperspeck, outbytesperspeck); } sl->sel = NewN( SelMask, nsp ); sl->nsel = nsp; memset(sl->sel, 0, nsp*sizeof(SelMask)); 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; #define LINEBUFSIZE 2048 static char *getline(FILE *f, char *buf) { buf[0] = '\0'; while(fgets(buf, LINEBUFSIZE, f) != NULL) { char *cp = buf; while(isspace(*cp)) cp++; if(*cp != '#' && *cp != '\0') return cp; } return NULL; } static enum SurfStyle getstyle( char *str ) { if(str == NULL || !strcmp(str, "solid") || !strncmp(str, "fill", 4) || !strncmp(str, "poly", 4)) return S_SOLID; if(!strncmp(str, "wire", 4) || !strncmp(str, "line", 4)) return S_LINE; if(!strncmp(str, "plane", 5) || !strncmp(str, "ax", 2)) return S_PLANE; if(!strncmp(str, "point", 5)) return S_POINT; if(!strcmp(str, "off")) return S_OFF; msg("Unknown surface style \"%s\": want solid|line|plane|point|off", str); return S_SOLID; } /* * Xcen Ycen Zcen ellipsoid [-t txno] [-c cindex] [-s surfstyle] [-n nu[,nv]] [-r sizex[,y,z]] [0-or-9-or-16-numbers-tfm] */ void specks_read_ellipsoid( struct stuff *st, Point *pos, int argc, char **argv, char *comment ) { int i; struct ellipsoid e, *ep; float m3[3*3]; float rxyz[3]; memset(&e, 0, sizeof(e)); e.cindex = -1; e.level = -1; e.pos = *pos; e.size.x[0] = e.size.x[1] = e.size.x[2] = 1; if(comment) { e.title = (comment[1] == ' ') ? comment+2 : comment+1; } for(i = 1; i+1 < argc; i += 2) { if(!strncmp(argv[i], "-c", 2)) { sscanf(argv[i+1], "%d", &e.cindex); } else if(!strncmp(argv[i], "-s", 2)) { e.style = getstyle(argv[i+1]); } else if(!strcmp(argv[i], "-r")) { int k = sscanf(argv[i+1], "%f%*c%f%*c%f", &e.size.x[0],&e.size.x[1],&e.size.x[2]); switch(k) { case 1: e.size.x[2] = e.size.x[1] = e.size.x[0]; break; case 3: break; default: msg("ellipsoid -r: expected 1 or 3 comma-separated semimajor axes not %s", argv[i+1]); } } else if(!strcmp(argv[i], "-n")) { int k = sscanf(argv[i+1], "%d%*c%d", &e.nu, &e.nv); switch(k) { case 1: e.nv = e.nu/2 + 1; break; case 2: break; default: msg("ellipsoid -n: expected nu or nu,nv but not %s", argv[i+1]); } } else if(!strcmp(argv[i], "-l")) { sscanf(argv[i+1], "%d", &e.level); } else { break; } } if(i == argc) { /* OK */ } else if(i+9 == argc && 9==getfloats(m3, 9, i, argc, argv)) { memcpy(&e.ori.m[0*4+0], &m3[0], 3*sizeof(float)); memcpy(&e.ori.m[1*4+0], &m3[3], 3*sizeof(float)); memcpy(&e.ori.m[2*4+0], &m3[6], 3*sizeof(float)); e.ori.m[3*4+3] = 1; e.hasori = 1; } else if(i+16 == argc && 16==getfloats(&e.ori.m[0], 16, i, argc, argv)) { e.hasori = 1; } else if(i+3 == argc && 3==getfloats(rxyz, 3, i, argc, argv)) { float aer[3]; aer[0] = rxyz[1]; aer[1] = rxyz[0]; aer[2] = rxyz[2]; xyzaer2tfm( &e.ori, NULL, aer ); e.hasori = 1; } else { msg("ellipsoid: expected 0 or 3 (RxRyRz) or 9 or 16 numbers after options, not %s", rejoinargs(i, argc, argv)); return; } /* OK, take it */ ep = NewN( struct ellipsoid, 1 ); *ep = e; if(e.title) ep->title = shmstrdup(e.title); ep->next = st->staticellipsoids; st->staticellipsoids = ep; } void specks_read_mesh( struct stuff *st, FILE *f, int argc, char **argv, char *buf ) { char *cp, *ep; int i, count, alloced, err = 0; int havetx = 0; Point pts[300], txs[300]; struct mesh *m = NewN( struct mesh, 1 ); int timestep = st->datatime; memset(m, 0, sizeof(*m)); m->type = QUADMESH; m->cindex = -1; m->txno = -1; m->pts = pts; m->tx = txs; alloced = COUNT(pts); if(!strcmp(argv[0], "quadmesh") || !strcmp(argv[0], "mesh")) { m->type = QUADMESH; } for(i = 1; i+1 < argc; i += 2) { if(!strncmp(argv[i], "-time", 3)) { timestep = atoi(argv[i+1]); } else if(!strncmp(argv[i], "-t", 2)) { havetx = sscanf(argv[i+1], "%d", &m->txno); } else if(!strcmp(argv[i], "-static")) { timestep = -1; i--; } else if(!strncmp(argv[i], "-c", 2)) { sscanf(argv[i+1], "%d", &m->cindex); } else if(!strncmp(argv[i], "-s", 2)) { m->style = getstyle(argv[i+1]); } else { break; } } if(m->type == QUADMESH) { getline(f, buf); if(sscanf(buf, "%d%d", &m->nu, &m->nv) != 2 || m->nu <= 0 || m->nv <= 0) { msg("quadmesh: expected nu nv, got %s", buf); Free(m); return; } alloced = m->nverts = m->nu * m->nv; m->pts = NewN( Point, m->nverts ); m->tx = havetx ? NewN( Point, m->nverts ) : NULL; } count = 0; while((cp = getline(f, buf)) != NULL) { int ngot; Point *tp; #define BRA '{' #define CKET '}' if(*cp == BRA) continue; if(*cp == CKET) break; if(count >= alloced) { if(m->type == QUADMESH) { err = 1; msg("Only expected %d*%d=%d vertices; what's %s", m->nu,m->nv, alloced, cp); break; } /* otherwise, ask for more */ /* XXX maybe later */ break; } tp = &m->pts[count]; for(ngot = 0; ngot < 3; ngot++, cp = ep) { tp->x[ngot] = strtod(cp, &ep); if(cp == ep) break; } if(havetx && ngot == 3) { tp = &m->tx[count]; for(ngot = 0; ngot < 2; ngot++, cp = ep) { tp->x[ngot] = strtod(cp, &ep); if(cp == ep) break; } ngot++; } if(ngot != 3) { msg("Expected %d numbers per line; what's %s", havetx?5:3, buf); err = 1; break; } count++; } if(m->type == QUADMESH && count < alloced) { err = 1; msg("Expected %d*%d = %d vertices, only got %d", m->nu,m->nv, alloced, count); } if(err) { if(m->pts != pts) Free(m->pts); if(m->tx != txs && m->tx != NULL) Free(m->tx); Free(m); return; } if(timestep >= 0) { if(timestep >= st->ntimes) specks_timespecksptr( st, st->curdata, timestep ); m->next = st->meshes[st->curdata][timestep]; st->meshes[st->curdata][timestep] = m; } else { m->next = st->staticmeshes; st->staticmeshes = m; } } void specks_read_waveobj( struct stuff *st, int argc, char **argv, char *line, char *infname ) { struct mesh *m = NewN( struct mesh, 1 ); int lno; int i, f; int timestep = st->datatime; FILE *inf; char *fname, *fullname; #define CHUNK 500 int kpts, kfvs, kfv0, kfvn; int npts, nfvs, nfv0, nfvn; struct chain { void *data; int pos; struct chain *next; } *cpts, *cfvs, *cfv0, *cfvn; Point vpts[CHUNK]; int vfvs[CHUNK], vfv0[CHUNK], vfvn[CHUNK]; memset(m, 0, sizeof(*m)); m->type = POLYMESH; m->cindex = -1; m->txno = -1; for(i = 1; i+1 < argc; i += 2) { if(!strncmp(argv[i], "-time", 2)) { timestep = atoi(argv[i+1]); } else if(!strncmp(argv[i], "-texture", 3) || !strncmp(argv[i], "-tx", 3)) { sscanf(argv[i+1], "%d", &m->txno); } else if(!strcmp(argv[i], "-static")) { timestep = -1; i--; } else if(!strncmp(argv[i], "-c", 2)) { sscanf(argv[i+1], "%d", &m->cindex); } else if(!strncmp(argv[i], "-s", 2)) { m->style = getstyle(argv[i+1]); } else { break; } } fname = argv[i]; fullname = findfile( infname, fname ); if(fullname == NULL || (inf = fopen(fullname, "r")) == NULL) { msg("waveobj: can't find .obj file %s", fname); return; } #define VINIT(what) c##what = NULL, k##what = n##what = 0 #define VTOTAL(what) (k##what + n##what) #define VNEXT(what) \ if(++k##what >= CHUNK) { \ struct chain *ct = (struct chain *)malloc(sizeof(*ct)); \ ct->data = malloc(sizeof(v##what)); \ memcpy(ct->data, v##what, sizeof(v##what)); \ ct->pos = n##what; \ ct->next = c##what; \ c##what = ct; \ k##what = 0; \ n##what += CHUNK; \ } #define VSTUFF(dest, what) { \ struct chain *ct, *cn; \ memcpy( &dest[n##what], v##what, k##what * sizeof(v##what[0]) ); \ for(ct = c##what; ct != NULL; ct = cn) { \ cn = ct->next; \ memcpy( &dest[ct->pos], ct->data, CHUNK*sizeof(v##what[0]) ); \ free(ct->data); \ free(ct); \ } \ } VINIT(pts); VINIT(fvs); VINIT(fv0); VINIT(fvn); lno = 0; while(fgets(line, LINEBUFSIZE, inf) != NULL) { char *word, *s = line; int slen; lno++; while(isspace(*s)) s++; if(*s == '#' || *s == '\0') continue; word = s++; while(*s != '\0' && !isspace(*s)) s++; slen = s - word; if(slen == 1 && word[0] == 'v') { Point *p = &vpts[kpts]; char *ep; p->x[0] = strtod(s, &s); p->x[1] = strtod(s, &s); p->x[2] = strtod(s, &ep); if(s == ep) { msg("waveobj: %s line %d: bad v line %s", fullname, lno, line); continue; } VNEXT(pts); } else if(slen == 1 && word[0] == 'f') { char *ep; int myfv0 = VTOTAL( fvs ); int myfvn = 0; for(;;) { while(isspace(*s)) s++; if(*s == '\0') break; vfvs[kfvs] = strtol(s, &ep, 10) - 1; if(s == ep) { msg("waveobj: %s line %d: bad f line %s", fullname, lno, line); myfvn = 0; break; } s = ep; while(!isspace(*s)) s++; VNEXT(fvs); myfvn++; } vfv0[kfv0] = myfv0; vfvn[kfvn] = myfvn; if(myfvn >= 3) { VNEXT(fv0); VNEXT(fvn); } } } fclose(inf); m->pts = NewN( Point, VTOTAL(pts) ); VSTUFF( m->pts, pts ); m->fv0 = NewN( int, VTOTAL(fv0) ); VSTUFF( m->fv0, fv0 ); m->fvn = NewN( int, VTOTAL(fvn) ); VSTUFF( m->fvn, fvn ); m->fvs = NewN( int, VTOTAL(fvs) ); VSTUFF( m->fvs, fvs ); m->nfaces = VTOTAL(fvn); m->nfv = VTOTAL(fvs); m->nverts = VTOTAL(pts); for(i = m->nfv; --i >= 0; ) { if(m->fvs[i] < 0 || m->fvs[i] >= m->nverts) { fprintf(stderr, "%s: vert index %d out of range 1..%d\n", fullname, m->fvs[i]+1, m->nverts); m->fvs[i] = 0; } } m->fnorms = NewN( Point, m->nfaces ); for(f = 0; f < m->nfaces; f++) { Point vab, vac, normal; int myfv0 = m->fv0[f]; if(m->fvn[f] > 3 && m->fvs[myfv0] == m->fvs[myfv0+1]) myfv0++; /* degenerate quad? */ vsub( &vab, &m->pts[ m->fvs[myfv0+1] ], &m->pts[ m->fvs[myfv0] ] ); vsub( &vac, &m->pts[ m->fvs[myfv0+2] ], &m->pts[ m->fvs[myfv0] ] ); vcross( &normal, &vab, &vac ); vunit( &m->fnorms[f], &normal ); } if(timestep >= 0) { if(timestep >= st->ntimes) specks_timespecksptr( st, st->curdata, timestep ); m->next = st->meshes[st->curdata][timestep]; st->meshes[st->curdata][timestep] = m; } else { m->next = st->staticmeshes; st->staticmeshes = m; } } void specks_read( struct stuff **stp, char *fname ) { FILE *f; char line[LINEBUFSIZE], oline[LINEBUFSIZE], *tcp; struct stuff *st = *stp; int maxfields = 0; #define SPECKCHUNK 2001 /* should be bigger, but too many machines have tiny stack areas! */ #define MAXARGS 48 int argc; char *argv[MAXARGS+1]; struct speck speckbuf[SPECKCHUNK]; struct speck *sp; struct speck s; struct specklist tsl; int i, nsp, maxnsp; int ignorefirst = 0; int lno = 0; char *comment; if(fname == NULL) return; tcp = NewA( char, strlen(fname)+1 ); /* fname's space might get reused */ strcpy(tcp, fname); fname = tcp; if((f = fopen(fname, "rb")) == NULL) { msg("%s: can't open: %s", fname, strerror(errno)); return; } tsl.bytesperspeck = (st->maxcomment+1 + (sizeof(s) - sizeof(s.title)) + 3) & ~3; s.rgba = 0; s.size = 1; nsp = 0; sp = speckbuf; maxnsp = sizeof(speckbuf) / tsl.bytesperspeck; #define SPFLUSH() \ if(nsp > 0) { \ addchunk( st, nsp, tsl.bytesperspeck, \ speckscale, speckbuf, NULL, \ maxfields >= MAXVAL ? tsl.bytesperspeck \ : SMALLSPECKSIZE(maxfields) ); \ } \ nsp = maxfields = 0; \ sp = speckbuf; 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); } argc = tokenize(line, oline, MAXARGS, argv, &comment); while(argc > 0 && !strcmp(argv[0], "add")) { for(i = 1; i <= argc; i++) argv[i-1] = argv[i]; argc--; } if(argc == 0) continue; if(nsp >= maxnsp || (isalnum(argv[0][0]) && !isdigit(argv[0][0]))) { SPFLUSH(); } if(!strcmp(argv[0], "include") || !strcmp(argv[0], "read")) { float oldscale = speckscale; char *infname = argv[1]; char *realfile = findfile( fname, infname ); if(realfile == NULL) { msg("%s: Can't find include-file %s", fname, infname); } else { specks_read( &st, realfile ); } speckscale = oldscale; } else if(!strcmp(argv[0], "object") && argc>1) { char *eq = strchr(argv[1], '='); i = 1; parti_object( argv[1], &st, 1 ); if(eq) { parti_set_alias( st, eq+1 ); } else if(argc >= 3 && !strcmp(argv[2], "=")) { /* object g3 = alias */ parti_set_alias( st, argv[3] ); } } else if(parti_read(stp, argc, argv, fname)) { st = *stp; /* In case st changed */ } else if(!strcmp(argv[0], "sdb") && argc>1) { int tno = st->datatime; char *realfile; i = 1; if(argc>3 && !strcmp(argv[1], "-t")) { if((tno = getfloat(argv[2], st->datatime)) < 0) { msg("sdb -t: expected timestepnumber(0-based), not %s", argv[2]); continue; } i = 3; } realfile = findfile( fname, argv[i] ); if(realfile == NULL) { msg("%s: sdb: can't find file %s", fname, argv[i]); } else { specks_read_sdb( st, realfile, tno ); } } else if(!strcmp(argv[0], "sdbvars")) { if(argc > 1) { if(argv[1][strspn(argv[1], "mMcrogtxyzSn")] != '\0') { msg("sdbvars: pattern must contain only chars from: mMcrogtxyzSn, not %s", argv[1]); } else { if(st->sdbvars) Free(st->sdbvars); st->sdbvars = shmstrdup(argv[1]); } } else { msg("sdbvars %s", st->sdbvars); } } else if(!strcmp(argv[0], "box") || !strcmp(argv[0], "boxes")) { Point cen, rad; struct AMRbox box; int k; int tno = -1; box.boxno = -1; box.level = 0; i = 1; while(i+2 < argc && argv[i][0] == '-') { if(argv[i][1] == 'n' && sscanf(argv[i+1], "%d", &box.boxno)>0) i += 2; else if(argv[i][1] == 'l' && sscanf(argv[i+1], "%d", &box.level)>0) i += 2; else if(argv[i][1] == 't' && (tno = getfloat(argv[i+1], st->datatime)) >= 0) i += 2; else break; } if(i+2 == argc && 3==sscanf(argv[i], "%f%*c%f%*c%f", &cen.x[0],&cen.x[1],&cen.x[2]) && 0<(k=sscanf(argv[i+1], "%f%*c%f%*c%f", &rad.x[0],&rad.x[1],&rad.x[2]))) { if(k<3) rad.x[1] = rad.x[2] = rad.x[0]; /* if scalar radius */ vsub(&box.p0, &cen, &rad); vadd(&box.p1, &cen, &rad); specks_add_box( st, &box, tno ); } else if(i+3 == argc && 2==sscanf(argv[i], "%f%*c%f",&box.p0.x[0],&box.p1.x[0]) && 2==sscanf(argv[i+1], "%f%*c%f",&box.p0.x[1],&box.p1.x[1]) && 2==sscanf(argv[i+2], "%f%*c%f",&box.p0.x[2],&box.p1.x[2])) { specks_add_box( st, &box, tno ); } else if(i+6 == argc) { k = getfloats( &box.p0.x[0], 3, i, argc, argv ) + getfloats( &box.p1.x[0], 3, i+3, argc, argv ); if(k==6) specks_add_box( st, &box, tno ); else msg("box: expected xmin ymin zmin xmax ymax zmax -or- xmin,xmax ymin,ymax zmin,zmax -or- xcen,ycen,zcen xrad,yrad,zrad"); } else if(i+1 == argc) { char *realfile = findfile( fname, argv[i] ); if(realfile == NULL) { msg("%s: boxes: can't find file %s", fname, argv[i]); } 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(argv[0], "annot", 5)) { static char ausage[] = "Usage: annot [-t timestep] string..."; int tno = st->datatime; i = 1; if(argc>1 && !strcmp(argv[1], "-t")) { tno = getfloat(argv[2], -1); i = 3; } if(i >= argc || tno < 0) { msg(ausage); continue; } specks_add_annotation( st, rejoinargs( i, argc, argv ), tno ); } else if(!strcmp(argv[0], "cment") || !strcmp(argv[0], "textcment") || !strcmp(argv[0], "boxcment")) { specks_parse_args( &st, argc, argv ); } else if(!strcmp(argv[0], "size") && argc>1) { sscanf(argv[1], "%f", &s.size); } else if(!strcmp(argv[0], "scale") && argc>1) { /* "scale" in data file, not as VIRDIR command */ float v = 0; sscanf(argv[1], "%f", &v); if(v != 0) st->spacescale = speckscale = v; } else if(!strcmp(argv[0], "tfm") && argc>1) { int inv = 0; int mul = 0; int hpr = 0; int camparent = -1; int more, j, k; float scl = 1; int has_scl = 0; Matrix ot, t; Point xyz; float aer[3]; char *key; i = 1; for(more = 1, key = argv[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 'c': camparent = 1; i++; more = 0; break; case 'w': camparent = 0; i++; more = 0; break; case '\0': more = 0; i++; break; default: more = 0; break; } } if(camparent>=0) parti_parent( st, camparent ? -1 : 0 ); parti_geto2w( st, parti_object( NULL, &st, 0 ), &ot ); k = getfloats( &t.m[0], 16, i, argc, argv ); switch(k) { 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 7: /* ignore fovy if included */ scl = t.m[6]; has_scl = 1; case 6: 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 ); if(has_scl) { for(i = 0; i < 12; i++) t.m[i] *= scl; } break; case 9: for(i = 2; i >= 0; i--) for(j = 2; j >= 0; j--) t.m[i*4+j] = t.m[i*3+j]; t.m[0*4+3] = t.m[1*4+3] = t.m[2*4+3] = t.m[3*4+0] = t.m[3*4+1] = t.m[3*4+2] = 0; t.m[3*4+3] = 1; break; case 16: break; case 0: if(camparent >= 0) continue; /* allow "tfm c" or "tfm w" */ default: msg("Usage: tfm: expected 1 or 6 or 9 or 16 numbers not %s", line); continue; } if(inv) eucinv( &t, &t ); if(mul) mmmul( &t, &ot, &t ); parti_seto2w( st, parti_object( NULL, &st, 0 ), &t ); } else if((!strcmp(argv[0], "eval") || !strcmp(argv[0], "feed") || !strcasecmp(argv[0], "VIRDIR")) && argc > 1) { #if THIEBAUX_VIRDIR if(0 == specks_parse_args( &st, argc-1, argv+1 )) { /* grr, need to skip first word of line */ char *s = line; while(isspace(*s)) s++; while(!isspace(*s) && *s) s++; VIDI_queue_commandstr( s ); } #else /* non-Thiebaux-virdir */ specks_parse_args( &st, argc-1, argv+1 ); #endif } else if(!strcmp(argv[0], "filepath")) { int k = 0; char **oldpath = getfiledirs(); char *path[128], *dir; for(i = 1, k = 0; i < argc && k < COUNT(path)-1; i++) { dir = strtok(argv[i], ":"); do { if(!strcmp(dir,"+")) { int m = 0; while(k<COUNT(path)-1 && oldpath && oldpath[m]) path[k++] = oldpath[m++]; } else { path[k++] = dir; } dir = strtok(NULL, ":"); } while(dir && k < COUNT(path)-1); } path[k] = NULL; if(k > 0) filedirs(path); } else if(!strcmp(argv[0], "setenv") && argc==3 && NULL==strchr(argv[1],'=')) { char *c = NewN( char, strlen(argv[1]) + strlen(argv[2]) + 2 ); sprintf(c, "%s=%s", argv[1], argv[2]); putenv(c); } else if(!strcmp(argv[0], "texture")) { int txno = -1; int txflags = TXF_SCLAMP | TXF_TCLAMP | TXF_ADD; int txapply = TXF_DECAL; int qual = 7; char *txfname, *key; i = 1; if(argc > 1 && sscanf(argv[1], "%d", &txno) > 0) i = 2; while(i < argc && (key = argv[i])[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, 'O')) txflags = (txflags & ~TXF_ADD) | TXF_OVER; if(strchr(key, 'M')) txapply = TXF_MODULATE; if(strchr(key, 'D')) txapply = TXF_DECAL; if(strchr(key, 'B')) txapply = TXF_BLEND; i++; } if((i < argc && txno < 0 && sscanf(argv[i++], "%d", &txno) <= 0) || (txfname = argv[i]) == NULL) { msg("Expected ``texture [-lmnaMDB] txno file.sgi'', got %s", line); msg(" opts: -l(inear) -m(ipmap) -n(earest) -i(intensity) -a(lpha) -A(dd) -O(ver(notAdd)) -M(odulate)|-D(ecal)|-B(lend))"); } else { txaddentry( &st->textures, &st->ntextures, fname, txno, txfname, txapply, txflags, qual ); } } else if(!strcmp(argv[0], "polyorivar") && argc == 2) { if(1!=specks_set_byvariable( st, argv[1], &st->polyorivar0 )) msg("polyorivar: unknown field %s", argv[1]); } else if(!strcmp(argv[0], "texturevar") && argc == 2) { if(1!=specks_set_byvariable( st, argv[1], &st->texturevar )) msg("texturevar: unknown field %s", argv[1]); } else if(!strncmp(argv[0], "coord", 5) || !strncmp(argv[0], "altcoord", 8)) { float *t = &st->altcoord[0].w2coord.m[0]; if(argc == 1) { 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 if(argc == 18) { sprintf(st->altcoord[0].name, "%.9s", argv[1]); i = getfloats( t, 16, 2, argc, argv ); if(i != 16) { msg("%s: expected 16 numbers; what's %s", argv[0],argv[i+2]); continue; } } else { msg("expected \"coord\" name ... 16 world-to-coord tfm floats (GL order) ..."); } } else if(!strncmp(argv[0], "dataset", 7) && argc == 3) { char name[12]; int indexno; if(sscanf(argv[1], "%d", &indexno) <= 0 || sscanf(argv[2], "%11s", name) <= 0 || 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(argv[0], "datavar", 7) && argc > 2) { int varno; struct valdesc vd; if(sscanf(argv[1], "%d", &varno)<=0 || sscanf(argv[2], "%19s", vd.name)<=0 || varno < 0 || varno >= MAXVAL) { msg("%s: expected ``datavar <indexno> <variablename> [minval maxval]'' with 0<=indexno<=%d", line, MAXVAL-1); continue; } strcpy(st->vdesc[st->curdata][varno].name, vd.name); if(argc == 5) { st->vdesc[st->curdata][varno].min = atof(argv[3]); st->vdesc[st->curdata][varno].max = atof(argv[4]); } } else if(!strcmp(argv[0], "datatime") && argc==2) { int newt = st->curtime; /* so e.g. "datatime now" => apply to current data */ sscanf(argv[1], "%d", &newt); if(sscanf(argv[1], "%d", &newt) <= 0 || newt < 0) { msg("%s: datatime %s: timestep must be >= 0", fname, argv[1]); continue; } if(newt == st->datatime) /* no need to change anything */ continue; SPFLUSH(); st->datatime = newt; } else if(!strcmp(argv[0], "mesh") || !strcmp(argv[0], "tstrip") || !strcmp(argv[0], "tfan")) { specks_read_mesh(st, f, argc, argv, line); } else if(!strcmp(argv[0], "waveobj")) { specks_read_waveobj(st, argc, argv, line, fname); } else if(!strcmp(argv[0], "textcolor") && argc==2) { sscanf(argv[1], "%d", &s.rgba); } else if(!strcmp(argv[0], "maxcomment") && argc==2) { int newmax = st->maxcomment; sscanf(argv[1], "%d", &newmax); if(newmax != st->maxcomment) { SPFLUSH(); st->maxcomment = newmax; tsl.bytesperspeck = (st->maxcomment+1 + (sizeof(s) - sizeof(s.title)) + 3) & ~3; maxnsp = sizeof(speckbuf) / tsl.bytesperspeck; } } else { struct valdesc *vdp; int k, m; k = getfloats( &s.p.x[0], 3, ignorefirst, argc, argv ); if(k < 3) { msg("Unrecognized datacmd: %s", line); continue; } i = ignorefirst + k; vdp = &st->vdesc[st->curdata][0]; k = getfloats( &s.val[0], COUNT(s.val), i, argc, argv ); for(m = 0; m < k; m++) { if(vdp->nsamples++ == 0) { vdp->min = vdp->max = s.val[m]; } else { if(vdp->min > s.val[m]) vdp->min = s.val[m]; else if(vdp->max < s.val[m]) vdp->max = s.val[m]; } vdp->sum += s.val[m]; vdp->mean = vdp->sum / vdp->nsamples; vdp++; } if(maxfields < k) maxfields = k; m = i+k; s.title[0] = '\0'; if(m < argc) { if(!strcmp(argv[m], "text")) { s.size = 1; if(++m < argc-1 && !strcmp(argv[m], "-size")) { sscanf(argv[m+1], "%f", &s.size); m += 2; } addchunk( st, 1, SMALLSPECKSIZE(0), speckscale, &s, rejoinargs(m, argc, argv), SMALLSPECKSIZE(0) ); } else if(!strcmp(argv[m], "ellipsoid")) { specks_read_ellipsoid( st, &s.p, argc-m, argv+m, comment ); } else { *sp = s; nsp++; sp = NextSpeck( speckbuf, &tsl, nsp ); } } else if(comment) { char *title = comment + (comment[1] == ' ' ? 2 : 1); *sp = s; strncpyt(sp->title, title, st->maxcomment+1); maxfields = MAXVAL+1; /* "keep titles too" */ nsp++; sp = NextSpeck( speckbuf, &tsl, nsp ); } else { *sp = s; nsp++; sp = NextSpeck( speckbuf, &tsl, 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, "rb"); 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; 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 ); parti_center( &pmid ); /* round to 1 decimal */ #if THIEBAUX_VIRDIR { Point pmid, fwd, headv, pos, offset, newpos; static Point zvec = {0,0,1}; float sz, scale; char cmd[128]; sz = vdist( &box->p0, &box->p1 ); sprintf(cmd, "%.1g", st->gscale * st->goboxscale * sz); scale = atof(cmd); 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, "rb"); 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] ); } } 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, "%.4f %.4f %.4f", 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, "%.4f %.4f %g", NULL); } return buf; } static Point zero = {0,0,0}, forward = {0,0,-1}; static void tellwhere(struct stuff *st) { Point pos, fwd; float aer[3], scl; Matrix cam2w; char buf[180]; #ifdef THIEBAUX_VIRDIR CAVENavConvertCAVEToWorld( zero.x, pos.x ); msg("cave at %s", whereis(st, buf, &pos, 0)); CAVEGetPosition( CAVE_HEAD_NAV, pos.x ); msg("head 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 scl = tfm2xyzaer( &pos, aer, &cam2w ); vtfmvector( &fwd, &forward, &cam2w ); msg("camera at %s", whereis(st, buf, &pos, 0)); msg("looking to %s", whereis(st, buf, &fwd, 1)); msg("jump %g %g %g %g %g %g %g", pos.x[0],pos.x[1],pos.x[2], aer[1],aer[0],aer[2], scl); msg("c2w: %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", cam2w.m[0], cam2w.m[1], cam2w.m[2], cam2w.m[3], cam2w.m[4], cam2w.m[5], cam2w.m[6], cam2w.m[7], cam2w.m[8], cam2w.m[9], cam2w.m[10], cam2w.m[11], cam2w.m[12], cam2w.m[13], cam2w.m[14], cam2w.m[15]); } 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 getfloats( float *v, int nfloats, int arg0, int argc, char **argv ) { int i; char *ep; for(i = 0; i < nfloats && arg0+i < argc; i++) { float tv = strtod( argv[arg0+i], &ep ); if(ep == argv[arg0+i]) break; v[i] = tv; } return i; } void editcmap( struct stuff *st, int ncmap, struct cment *cmap, char *range, char *whose, char *colorstr ) { int i, min, max; char *cp; struct cment cm; cp = colorstr; while(isspace(*cp)) cp++; if(*cp == ':' || *cp == '=') { char junk; do cp++; while(*cp == '='); if(sscanf(cp, "%d%c", &i, &junk) != 1) { msg("%s: expected entrynumber(s) := oldindex, not %s", whose, colorstr); return; } if(i < 0 || i >= ncmap) { msg("%s: clamping index %d to 0..%d", whose, i, ncmap-1); i = (i < 0) ? 0 : ncmap-1; } cm = cmap[i]; } else { float r, g, b; if(sscanf(cp, "%f%f%f", &r,&g,&b) != 3) { msg("%s: expected entrynumber(s) r g b, not %s", whose, colorstr); return; } cm.raw = PACKRGBA( (int)(r*255), (int)(g*255), (int)(b*255), 0 ); cm.cooked = specks_cookcment( st, cm.raw ); } for(cp = range; ; cp++) { while(isspace(*cp) || *cp == ',') cp++; if((i = sscanf(cp, "%d-%d", &min, &max)) > 0) { if(i == 1) max = min; } else if((i = sscanf(cp, ">%d", &min)) > 0) { max = ncmap-1; } else if((i = sscanf(cp, "<%d", &max)) > 0) { min = 0; } if(i > 0) { if(min < 0) min = 0; if(max >= ncmap) max = ncmap-1; for(i = min; i <= max; i++) cmap[i] = cm; } while(*cp != ',' && !isspace(*cp)) { if(*cp == '\0') return; cp++; } } } int specks_parse_args( struct stuff **stp, int argc, char *argv[] ) { int i; struct stuff *st = *stp; if(parti_parse_args( stp, argc, argv, NULL ) > 0) return 1; while( argc>0 && (!strncmp( argv[0], "specks", 4 ) || !strcmp( argv[0], "feed" ) || !strcmp( argv[0], "eval" )) ) { argc--, argv++; /* VD_select_menu( specks_menuindex ); */ } if(argc <= 0) return 0; if( st->dyn.enabled && st->dyn.ctlcmd && (*st->dyn.ctlcmd)( &st->dyn, st, argc, argv ) ) { /* OK, dyn command handled it */ } else 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/step)", " 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 brightness LUM", " slum SCALEFACTOR scale particle brightness by SCALEFACTOR", " psize SIZE scale particle brightness by SIZE * SCALEFACTOR", " depthsort sort polygons by depth", " see DATASETNO-or-NAME show that dataset (e.g. \"seedata 0\" or \"seedata 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" " 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}", #if CAVEMENU " 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); if(st->dyn.enabled && st->dyn.help) (*st->dyn.help)(&st->dyn, st, 0); } else if(!strcmp( argv[0], "read" )) { if(argc > 1) { char *foundfile = findfile( NULL, argv[1] ); if(foundfile == NULL) msg("read: can't find file %s", argv[1]); else specks_read( &st, foundfile ); } } 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], "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" ) || !strcmp( argv[0], "|" )) { specks_add_async( st, rejoinargs( 1, argc, argv ), argv[0][0] == '|' ); } else if(!strcmp( argv[0], "update" )) { parti_update(); } else if(!strcmp( argv[0], "hist" )) { register struct specklist *sl; register struct speck *sp; 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; Matrix To2w; int hasT = 0; if(argc > 1) { hasT = 1; parti_geto2w( st, parti_object( NULL, &st, 0 ), &To2w ); } 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 )) { Point tp, *p = &sp->p; if(hasT) { vtfmpoint( &tp, p, &To2w ); p = &tp; } if(xmin.x[0] > p->x[0]) xmin.x[0] = p->x[0]; if(xmax.x[0] < p->x[0]) xmax.x[0] = p->x[0]; mean.x[0] += p->x[0]; if(xmin.x[1] > p->x[1]) xmin.x[1] = p->x[1]; if(xmax.x[1] < p->x[1]) xmax.x[1] = p->x[1]; mean.x[1] += p->x[1]; if(xmin.x[2] > p->x[2]) xmin.x[2] = p->x[2]; if(xmax.x[2] < p->x[2]) xmax.x[2] = p->x[2]; mean.x[2] += p->x[2]; } nspecks += sl->nspecks; } if(nspecks == 0) { msg("No specks loaded yet"); } else { char *which = hasT ? " (world)" : " (object)"; 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%s", nspecks, xmin.x[0],xmin.x[1],xmin.x[2], xmax.x[0],xmax.x[1],xmax.x[2], which); msg( "midbbox %g %g %g boxradius %g %g %g%s", mid.x[0],mid.x[1],mid.x[2], radius.x[0],radius.x[1],radius.x[2], which); msg( "mean %g %g %g%s", mean.x[0],mean.x[1],mean.x[2], which ); } } 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], "timealign" )) { float datatime = 0, realtime = 0; /* Compute timebase so datastep <datatime> falls at <clocktime>, * given current speed setting. */ double speed = clock_speed(st->clk) * st->clk->fwd; double timebase; if(getfloats( &datatime, 1, 1, argc,argv ) > 0 && getfloats( &realtime, 1, 2, argc,argv ) > 0) { specks_set_timebase( st, datatime - realtime*speed ); msg("Aligning datastep %g at realtime %g", datatime, realtime); } else if(argc>1) { msg("Usage: timealign <datatime> <realtime>"); } timebase = clock_timebase(st->clk); if(speed == 0) msg("timebase %g (always at that datastep since speed=0)", timebase); else msg("timebase %g (datastep at realtime 0), or datastep %g at realtime %g", timebase, datatime, (datatime - timebase) / speed); } else if(!strcmp( argv[0], "timebase" )) { if(argc>1) specks_set_timebase( st, getfloat(argv[1], clock_timebase(st->clk)) ); msg("timebase %g (datastep at time 0)", clock_timebase(st->clk)); } else if(!strcmp( argv[0], "skipblanktimes" )) { if(argc>1) st->skipblanktimes = getbool(argv[1], st->skipblanktimes); msg("blanktimes %s", st->skipblanktimes ? "on (skip blank timesteps)" : "off (show blankness at blank timesteps)"); } 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); if(!strcmp(argv[1], "hide")) st->clipbox.level = -1; 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 ); if(st->clipbox.level==0) 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; if(st->clipbox.level==0) 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) { if(st->clipbox.level == 0) st->clipbox.level = 1; } else { msg("clipbox: xmin ymin zmin xmax ymax zmax"); } break; default: msg("clipbox: on|off|hide | x0,x1 y0,y1 z0,z1 | Cenx,y,z Rx,y,z | x0 y0 z0 x1 y1 z1"); 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 ? (st->clipbox.level<0 ? "hide":"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, 0 ); ep = argv[a] ? strchr(argv[a], '=') : NULL; if(a && argc==1) { msg(tst->alias ? "object g%d=%s" : "object g%d", i, tst->alias); } else 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 )) { int verbose = (argc>1 && !strcmp(argv[1], "-v")); parti_allobjs( argc-1-verbose, argv+1+verbose, verbose ); } 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; int has_scl; Matrix ot, t; Point xyz; float aer[3]; char *key; int verbose = 0; i = 1; if(argc > 1 && !strcmp(argv[1], "-v")) { verbose = 1, i++; } 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, 0 ), &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 7: scl = t.m[6]; has_scl = 1; /* and fall into... */ case 6: 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 ); if(has_scl) { for(i = 0; i < 12; i++) t.m[i] *= scl; } break; case 16: break; case 0: t = ot; break; default: msg("Usage: tfm [*] [/] [\"hpr\"] [16 numbers or 6 or 7 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, 0 ), &t ); } scl = tfm2xyzaer( &xyz, aer, &t ); msg("obj2w: x y z %s %g %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], scl ); if(verbose) { for(i = 0; i < 3; i++) msg(" %10.7g %10.7g %10.7g %10.7g", 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 ); scl = tfm2xyzaer( &xyz, aer, &t ); msg("w2obj: x y z %s %g %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], scl ); 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 } 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); #endif #if MATT_VIRDIR } else if(!strcmp( argv[0], "time" )) { float newnow, now; if(argc > 1 && sscanf(argv[1], "%f", &newnow) > 0) now = vd_camera_time( &newnow ); else now = vd_camera_time( NULL ); msg("time %g", now); } else if(!strcmp( argv[0], "key" )) { int curkey; if(argc == 1) { curkey = vd_create_key(); } else { curkey = vd_select_key( atoi(argv[1]), 0 ); } msg("key %d", curkey); } else if(!strcmp( argv[0], "attach" )) { vd_attach( getbool( argv[1], 1 ) ); } else if(!strcmp( argv[0], "rem" )) { vd_remove_key( argc, argv ); #endif /* MATT_VIRDIR */ } else if(!strcmp( argv[0], "step" )) { 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) ); IFCAVEMENU( set_fwd( clock_fwd(st->clk), NULL, 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; struct mesh *m, *nm; struct ellipsoid *e, *ne; slp = specks_timespecksptr( st, st->curdata, st->curtime ); specks_discard( st, slp ); st->sl = *slp; for(m = st->staticmeshes, st->staticmeshes = NULL; m; m = nm) { nm = m->next; Free(m); } for(e = st->staticellipsoids, st->staticellipsoids = NULL; e; e = ne) { ne = e->next; Free(e); } } 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], "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(!strcmp( argv[0], "vcmap" )) { int k = 1; int coloredby = st->coloredby; char *tfname, *realfile; struct valdesc *vd; if(argc > 3 && !strcmp( argv[1], "-v" )) { if(!specks_set_byvariable( st, argv[2], &coloredby )) { msg("vcmap -v %s: unknown colorby-variable", argv[2]); return -1; } k = 3; } vd = &st->vdesc[st->curdata][coloredby]; if(coloredby < 0 || coloredby >= MAXVAL) { msg("vcmap: bad colorby-variable %d", coloredby); return -1; } if(argc != k+1) { msg("Usage: vcmap [-v varname] <ascii-file-of-RGB-triples>", argv[0]); } else { tfname = argv[k]; realfile = findfile( NULL, tfname ); if(realfile == NULL) { tfname = (char *)alloca( strlen(argv[k]) + 6 ); sprintf(tfname, "%s.cmap", argv[k]); realfile = findfile( NULL, tfname ); } if(realfile != NULL) { specks_read_cmap( st, realfile, &vd->vncmap, &vd->vcmap ); st->colorseq++; /* Must recompute particle coloring */ } else { msg("vcmap: can't find \"%s\" nor ....cmap", argv[k]); } } } else if(!strncmp( argv[0], "cment", 5 ) || !strncmp( argv[0], "boxcment", 8 ) || !strncmp( argv[0], "textcment", 6 )) { int i; 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 && argv[1][0] != '?') { 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>2) { char *colorstr = rejoinargs( 2, argc, argv ); editcmap( st, ncmap, cmap, argv[1], argv[0], colorstr ); 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] -or- c1-c2,c3,c4 r g b -or- c1-c2,... = cindex", prefix); msg(" (no blanks around commas for color ranges)"); } } 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 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; st->selseq++; if(exact || all) { /* preset all values */ SelMask selbit = (plus || all<0) ? 0 : SELMASK(SEL_THRESH); for(sl = st->sl; sl != NULL; sl = sl->next) { SelMask *sel = sl->sel; if((sp = sl->specks) == NULL || sl->text != NULL || sel == NULL) continue; for(k = sl->nspecks; --k >= 0; sp = NextSpeck(sp, sl, 1)) { sel[k] = (sel[k] & ~SELMASK(SEL_THRESH)) | selbit; } sl->selseq = st->selseq; } } /* * If a thresh variable is given, use "plus" to choose * whether to make that speck visible. */ 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) { SelMask *sel = sl->sel; if((sp = sl->specks) == NULL || sl->text != NULL || tvar<0 || SMALLSPECKSIZE(tvar+1) > sl->bytesperspeck) continue; for(k = 0; k < sl->nspecks; sp = NextSpeck(sp, sl, 1), k++) { if(sp->val[tvar]>=vmin && sp->val[tvar]<=vmax) { sel[k] = plus ? sel[k] | SELMASK(SEL_THRESH) : sel[k] & ~SELMASK(SEL_THRESH); } } sl->selseq = st->selseq; } } parse_selexpr( st, "thresh", &st->threshsel, NULL, NULL ); seldest2src( st, &st->threshsel, &st->seesel ); msg("only %s (selecting \"see %s\")", selcounts( st, st->sl, &st->threshsel ), show_selexpr( st, NULL, &st->seesel )); } else if(!strncmp( argv[0], "thresh", 6 )) { int i, expect = -1; float v; char smin[16], smax[16], *arg; char chosen[40]; int changed = 0; char *selstr = NULL; for(i = 1; i < argc; i++) { arg = argv[i]; if(!strcmp(arg,"-s")) { selstr = argv[++i]; } else 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) { st->threshseq++; st->selseq++; if(!parse_selexpr( st, selstr ? selstr : "thresh", &st->threshsel, NULL, NULL )) { msg("thresh -s %s: expected selection variable name", selstr); return 1; } /* "thresh" with no -s implies also "see thresh" */ if(selstr == NULL && st->seesel.wanted == 0) seldest2src( st, &st->threshsel, &st->seesel ); specks_reupdate( st, st->sl ); } chosen[0] = '\0'; sprintf(chosen, " (%s selected)", selcounts( st, st->sl, &st->threshsel )); 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%s%s %d(%s) min %s max %s%s", selstr ? " -s " : "", selstr ? selstr : "", st->usethresh&P_USETHRESH ? "" : " off ", st->threshvar, st->vdesc[st->curdata][st->threshvar].name, smin, smax, chosen); } else if(!strcmp( argv[0], "rawdump" )) { int all = 0; FILE *f; struct specklist *sl; int i, j, nmine, net; if(argc>1 && !strcmp(argv[1], "-a")) { all = 1, argc--, argv++; } f = argc>1 ? fopen(argv[1], "w") : NULL; if(f == NULL) { msg("%s: can't create", argv[1]); } else { fprintf(f, "# from dataset %d \"%s\" timestep %d time %g\n", st->curdata, CURDATATIME(fname), st->curtime, st->currealtime); if(all) fprintf(f, "x y z\txRGB\tSize\t"); 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++) { struct speck *sp = NextSpeck(sl->specks, sl, j); if(all) { fprintf(f, "%.8g %.8g %.8g\t%02x%02x%02x\t%g", sp->p.x[0],sp->p.x[1],sp->p.x[2], RGBA_R(sp->rgba), RGBA_G(sp->rgba), RGBA_B(sp->rgba), sp->size); } if(nmine > 0) fprintf(f, "%g", sp->val[0]); for(i = 1; i < nmine; i++) fprintf(f, "\t%g", sp->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 *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], "seedata" )) { 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(!strncmp( argv[0], "seesel", 3 )) { if(argc>1) parse_selexpr( st, rejoinargs( 1, argc, argv ), NULL, &st->seesel, "see" ); msg("see %s (%s)", show_selexpr( st, NULL, &st->seesel ), selcounts( st, st->sl, &st->seesel )); } else if(!strcmp( argv[0], "emph" )) { if(argc>2 && !strcmp(argv[1], "-f")) st->emphfactor = getfloat( argv[2], st->useemph ), argc -= 2, argv += 2; if(argc>1) { if(!strcmp(argv[1], "on")) st->useemph = 1; else if(!strcmp(argv[1], "off")) st->useemph = 0; else { parse_selexpr( st, rejoinargs( 1, argc, argv ), NULL, &st->emphsel, "emph" ); st->useemph = 1; } } if(st->useemph) { st->sizeseq++; msg("emph -f %g %s (%s emphasized)", st->emphfactor, show_selexpr( st, NULL, &st->emphsel ), selcounts( st, st->sl, &st->emphsel )); } else { msg("emph off"); } } else if(!strcmp( argv[0], "sel" )) { SelOp src, dest; char *rest = rejoinargs( 1, argc, argv ); /* * sel a = b -c d (existing names, or "see" or "emph" or maybe "thresh") * sel a = [all|none] */ if(strchr(rest, '=')) { if(parse_selexpr( st, rest, &dest, &src, "sel" )) { struct specklist *sl; /* Is this the right place to look? Do we want to update more than st->sl? */ int selcount = 0; st->selseq++; for(sl = st->sl; sl != NULL; sl = sl->next) { int k, ns = sl->nspecks; SelMask *sel = sl->sel; if(sl->text != NULL || sl->nsel < ns) continue; for(k = 0; k < ns; k++) { if(src.use != SEL_NONE && SELECTED(sel[k], &src)) { SELSET(sel[k], &dest); selcount++; } } if(st->useemph) specks_resize( st, sl, sl->sizedby ); /* XXX could anything else depend on this? */ sl->selseq = st->selseq; } msg("sel %s : %d of %d selected", show_selexpr(st, &dest, &src), selcount, specks_count(st->sl)); } } else { /* No '=' -- just asking how many match some pattern */ if(parse_selexpr( st, rest, NULL, &src, "sel")) { msg("sel %s : %s matched", show_selexpr( st, NULL, &src ), selcounts(st, st->sl, &src)); } } } else if(!strcmp( argv[0], "pick" )) { /* pick [-a] [note|center|sel <name>] */ /* kira pick track */ msg("pick not yet implemented XXX"); } 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. */ IFCAVEMENU( partimenu_boxlevels( st ) ); } 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 ); IFCAVEMENU( set_psize( st->psize, NULL, 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); IFCAVEMENU( set_polysize( st->polysize, NULL, 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 ); IFCAVEMENU( set_point( st->usepoint, NULL, 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 ); IFCAVEMENU( set_poly( st->usepoly, NULL, 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 if(argc > 1 && !strncmp(argv[1], "report", 3)) { if(argc == 2) { for(i = 0; i < st->ntextures; i++) if(st->textures[i]) st->textures[i]->report = ~0; } else { for(i = 2; i < argc; i++) { int t = atoi(argv[i]); if(t >= 0 && t < st->ntextures && st->textures[t]) st->textures[t]->report = ~0; } } } 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(!strncmp( argv[0], "ellipsoids", 5 )) { st->useellipsoids = getbool(argv[1], !st->useellipsoids); msg("ellipsoids %s", st->useellipsoids ? "on":"off"); } else if(!strncmp( argv[0], "meshes", 4 )) { st->usemeshes = getbool(argv[1], !st->usemeshes); msg("meshes %s", st->usemeshes ? "on":"off"); } else if(!strncmp( argv[0], "mullions", 3 )) { st->mullions = getfloat( argv[1], st->mullions ); msg("mullions %g", st->mullions); } else if(!strcmp( argv[0], "label" ) || !strcmp( argv[0], "labels" )) { st->usetext = getbool( argv[1], !st->usetext ); IFCAVEMENU( set_label( st->usetext, NULL, 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) st->alpha = getfloat(argv[1], st->alpha); IFCAVEMENU( set_alpha( st->alpha, NULL, 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) { IFCAVEMENU( set_fog( st->fog, NULL, st ) ); } else if(!strcmp( argv[0], "menu" ) || !strcmp( argv[0], "fmenu" )) { IFCAVEMENU( partimenu_fmenu( st, argc, argv ) ); } 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 if(!strcmp( argv[0], "version" )) { extern char partiview_version[]; char *cp = strchr(local_id, ','); /* ... partibrains.c,v N.NN */ if(cp) { cp += 3; msg("version %s (brains %.*s)", partiview_version, strcspn(cp, " "), cp); } } else return 0; *stp = st; /* in case it changed, e.g. because of an "object" cmd */ return 1; }