Skip to content
Snippets Groups Projects
partiviewc.cc 40.3 KiB
Newer Older
/*
 * UI-related glue functions for partiview.
 * Stuart Levy, slevy@ncsa.uiuc.edu
 * National Center for Supercomputing Applications,
 * University of Illinois 2001.
 * This file is part of partiview, released under the
 * Illinois Open Source License; see the file LICENSE.partiview for details.
#include <stdio.h>
#include <stdlib.h>

slevy's avatar
slevy committed
#ifdef _WIN32
#include "winjunk.h"
#endif

#ifdef __APPLE__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>    /* for GLuint */
#endif

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

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

#ifndef FLHACK
# include <FL/glut.H>	/* for GLUT_STEREO if FLTK knows it */
#endif
#ifndef GLUT_STEREO
# if unix
#  include <FL/x.H>	/* otherwise, need GLX symbols for X-specific stereo */
#  include <GL/glx.h>
# endif
#endif


#include <ctype.h>
#undef isspace		/* hack for irix 6.5 */
#undef isdigit
#ifdef FLHACK
extern void _pview_pickmsg( const char *fmt, ... );
#endif

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

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

    if(centerit) {
slevy's avatar
slevy committed
	view->center( &wpos );
	if(ppui.censize > 0) view->redraw();
    }
  } else {

#ifdef FLHACK
    _pview_pickmsg("Picked nothing (%d hits)", nhits);
#else
    msg("Picked nothing (%d hits)", nhits);
  }
}

void draw_specks( Fl_Gview *view, void *vst, void *junk ) {
  struct stuff *st = (struct stuff *)vst;
  if(st->useme) {
    specks_set_timestep( st );
    specks_current_frame( st, st->sl );
    st->inpick = view->inpick();
    drawspecks( st );
    st->inpick = 0;
  }
void parti_allobjs( int argc, char *argv[], int verbose ) {
  if(parti_parse_args( &st, argc, argv, NULL ))
slevy's avatar
 
slevy committed
  for(int i = 0; i < MAXSTUFF; i++) {
	const char *onoff = st->useme ? "on" : "off";
	if(verbose) {
	    if(st->alias)  msg("g%d=%s: (%s)", i, st->alias, onoff);
	    else msg("g%d: (%s)", i, onoff);
	}
slevy's avatar
 
slevy committed
	specks_parse_args( &st, argc, argv );
slevy's avatar
 
slevy committed
  }
}

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

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

float parti_getcensize() {
  return ppui.censize;
}

char *parti_bgcolor( const char *rgb ) {
slevy's avatar
slevy committed
  static char bgc[20];
  Point bgcolor;

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

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

slevy's avatar
slevy committed
static void set_wanted_dspcontext( Fl_Gview *view, int detached )
{
  view->dspcontext( (detached ? 2 : 0) + (view->stereo() == GV_QUADBUFFERED ? 1 : 0) );
}


static int stereo_mode = FL_STEREO | FL_RGB | FL_DOUBLE | FL_DEPTH | FL_MULTISAMPLE;

int try_quad_stereo()
    return(Fl_Gl_Window::can_do( stereo_mode ));
}

#else  /* backward compatibility -- quadbuffered stereo under Unix/X only */

static int *stereo_mode = NULL;


static int attrs[] = {
#ifdef GLX_SAMPLE_BUFFERS_SGIS
    GLX_SAMPLE_BUFFERS_SGIS, 1,
    GLX_SAMPLES_SGIS, 4,
#endif
    GLX_STEREO,
    GLX_RGBA,
    GLX_DOUBLEBUFFER,
    GLX_RED_SIZE, 1,
    GLX_GREEN_SIZE, 1,
    GLX_BLUE_SIZE, 1,
    GLX_DEPTH_SIZE, 1,
    None
int try_quad_stereo()
{
    stereo_mode = attrs;
#ifdef GLX_SAMPLE_BUFFERS_SGIS
    if(Fl_Gl_Window::can_do( stereo_mode ))
	return 1;
    stereo_mode += 4;
#endif
    if(Fl_Gl_Window::can_do( stereo_mode ))
	return 1;
    return 0;
}
int try_quad_stereo() { return 0; }
#endif

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

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

  if(ster) {
    float v;
slevy's avatar
slevy committed
    int pixoff;
slevy's avatar
slevy committed
    const char *cp = strchr(ster, '@');
slevy's avatar
slevy committed
    if(cp && sscanf(cp+1, "%d", &pixoff)) {
	ppui.view->stereooffset(pixoff);
	smode = stereotype;
    }
slevy's avatar
slevy committed
    const char *sp = strpbrk(ster, "-.0123456789");
slevy's avatar
slevy committed
    if(sp && (cp==NULL || sp<cp) && sscanf(sp, "%f", &v)) {
	ppui.view->stereosep(v);
	smode = stereotype;
    }
    if(strstr(ster,"on"))
	smode = stereotype;
    if(strstr(ster,"off"))
	smode = GV_MONO;
    if(strstr(ster, "red"))
	smode = stereotype = GV_REDCYAN;
    if(strstr(ster, "quad") || strstr(ster, "gla") ||
				strstr(ster, "hard") || strstr(ster, "hw")) {
	if(quadmode > 0) {
	    smode = stereotype = GV_QUADBUFFERED;
	} else {
	    msg("Can't do quadbuffered/hardware stereo");
	    smode = GV_MONO;
	}
    }
    else if(strstr(ster, "cross"))
slevy's avatar
slevy committed
	smode = stereotype = GV_CROSSEYED;
    else if(strstr(ster, "left"))
	smode = stereotype = GV_LEFTEYE;
    else if(strstr(ster, "right"))
	smode = stereotype = GV_RIGHTEYE;
    if(smode < 0) {
	msg("stereo: [on|off|redcyan|glasses|crosseyed|left|right] [stereosep[@pixeloffset]]");
	msg(" Try stereosep values from 0.02 to 0.1, or -.02 .. -.1 to swap eyes");
    }
  }

  if(smode >= 0) {
    if(smode == (int)GV_QUADBUFFERED)
    else
        ppui.view->glmode( FL_RGB | FL_DOUBLE | FL_DEPTH | FL_MULTISAMPLE );
    ppui.view->stereo( (enum Gv_Stereo)smode );
    set_wanted_dspcontext( ppui.view, (ppui.view == ppui.freewin)/*detached*/ );
  }

  smode = ppui.view->stereo();
	ppui.view->stereosep(),
	(smode<=0||smode>=COUNT(sternames)) ? "off" : sternames[smode]);
slevy's avatar
slevy committed
  int pixoff = ppui.view->stereooffset();
  if(pixoff)
	snprintf(rslt+strlen(rslt), sizeof(rslt)-strlen(rslt), "@%d", pixoff);
slevy's avatar
slevy committed
  
  return rslt;
}

int y_root( Fl_Widget *w ) {
  if(w == NULL) return -1;
  int y = w->y();
  Fl_Window *pa;
  for(pa = w->window(); pa != NULL; pa = pa->window())
    y += pa->y();
  return y;
}

Fl_Gview *make_view_window() {
  if(ppui.freewin == NULL) {
    Fl_Group::current( NULL );
    Fl_Double_Window *o = new Fl_Double_Window(512, 512);
    ppui.freemain = o;
    ppui.freewin = new Fl_Gview(0, 0, 512, 512);
    o->add( ppui.freewin );
    Fl_Group::current( NULL );
  }
  return ppui.freewin;
}
void parti_detachview( const char *how ) {
  int seeui = 1;
  if(how == NULL) how = "fullscreen";
  Fl_Gview *w = ppui.view;

//fprintf(stderr, "detach(%s)\n", how);
  switch(how[0]) {
  case 'h': /* full screen, hidden UI */
	seeui = 0;
  case 'f': /* fullscreen detached window */
	w = make_view_window();
	ppui.freemain->fullscreen();
	parti_update();
	ppui.freemain->show();
	parti_update();
	w->resize( 0, 0, ppui.freemain->w(), ppui.freemain->h() );
	ppui.freemain->init_sizes();
	break;

  case 'd': /* ordinary detached window */
	w = make_view_window();
	if(ppui.view && ppui.view->w() * ppui.view->h() > 0) {
	    ppui.freemain->fullscreen_off(50, 50, ppui.view->w(), ppui.view->h());
	    w->size( ppui.view->w(), ppui.view->h() );
	} else {
	    ppui.freemain->fullscreen_off(50, 50, 512, 512);
	}
	ppui.freemain->show();
	if(w->window() && w->window()->visible()) w->show();
slevy's avatar
slevy committed
	msg("detach: huh?  expected \"detach fullscreen\" or \"detach hide\" or just \"detach\"");
  if(w != NULL) {
    int mainy0 = y_root( ppui.mainwin );
    int viewy0 = y_root( ppui.cmd ) + ppui.cmd->h();
    int mainheight = viewy0 - mainy0;
    Fl_Widget *resiz = ppui.maintile->resizable();
    if(resiz && resiz != ppui.maintile) {
	ppui.maintile->resizable(ppui.maintile);
	ppui.maintile->remove( resiz );
    }
    ppui.mainwin->size( ppui.mainwin->w(), mainheight );
    ppui.mainwin->init_sizes();
    ppui.maintile->init_sizes();
    ppui.maintile->redraw();
    ppui.mainwin->redraw();
    // parti_update();
    if(w != ppui.view) {
	w->initfrom( ppui.view );
	ppui.view = w;
	set_wanted_dspcontext( ppui.view, 1 );
	/* Fix up dangling references to old view */
	ppui.view->picker( specks_picker, ppui.view );
	if(ppui.hrdiag) ppui.hrdiag->picker( specks_picker, ppui.view );
    }
    ppui.detached = how[0];	// 'h' or 'f' or 'd'
    parti_redraw();
    ppui.boundwin->hide();
    if(seeui) {
	ppui.mainwin->set_non_modal();
	ppui.mainwin->show();		// pop control strip to top
    } else {
	ppui.view->show();
	ppui.mainwin->hide();
    }

  /* Is there a main-window-position spec included in our param? */
  for(cp = how; *cp; cp++) {
      if(*cp == '-' || *cp == '+') {
	  char cx[2], cy[2];
	  int xpos, ypos;
	  if(4 == sscanf(cp, "%1[-+]%d%1[-+]%d", cx, &xpos, cy, &ypos)) {
	      Fl_Window *mw = ppui.mainwin;
	      mw->position( cx[0]=='+' ? xpos : Fl::w() - xpos - mw->w(),
		      	    cy[0]=='+' ? ypos : Fl::h() - ypos - mw->h() );
	      break;
	  }
      }
  }

char *parti_winsize( CONST char *newsize ) {
  snprintf(cursize, sizeof(cursize), "-1x-1");
  int posx = 0, posy = 0, has_pos = 0;
//fprintf(stderr, "winsize(%s)\n", newsize);
  Fl_Window *top = (ppui.freewin != NULL) ?
		ppui.freemain : ppui.mainwin;
  int ox = ppui.view->w(), oy = ppui.view->h();
slevy's avatar
slevy committed
  if(!top->shown()) {
    int nx = ox, ny = oy;
slevy's avatar
slevy committed
    const char *posp = strpbrk(newsize, "-+");
    char cx[2], cy[2];
    if(posp != newsize && sscanf(newsize, "%d%*c%d", &nx, &ny) == 1)
	ny = nx * oy / ox;
    if(posp && 4 == sscanf(posp, "%1[-+]%d%1[-+]%d", cx, &posx, cy, &posy)) {
	if(cx[0] == '-') posx = Fl::w() - nx - posx;
	if(cy[0] == '-') posy = Fl::h() - ny - posy;
    if(ox < 0) {		/* Not yet available -- remember for later */
	strncpy(cursize, newsize, sizeof(cursize)-1);
	ppui.reqwinsize = cursize;
	return cursize;
    }
    if(nx != ox || ny != oy || has_pos) {
	if(ppui.freewin != NULL) {
	    parti_detachview( ppui.detached=='h' ? "hide" : "detach" );
	    top->border( 1 );
	    if(has_pos) top->position( posx, posy );
	    top->size( nx, ny );
slevy's avatar
slevy committed
	    ppui.view->size( nx, ny );
	    if(has_pos)
		top->position( posx, posy );
slevy's avatar
slevy committed
	    top->size( top->w() + nx - ox,
		       top->h() + ny - oy );
	    ppui.view->size( nx, ny );
	if(top->visible())
	    ppui.view->show();
	parti_update();
	return parti_winsize(NULL);
    }
  }
  snprintf(cursize, sizeof(cursize), has_pos ? "%dx%d+%d+%d" : "%dx%d", ox, oy, posx, posy);
  return cursize;
}

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

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

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

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

  return ppui.pickrange;
}

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

int parti_snapset( char *fname, char *frameno, char *imgsize )
{
  int len;
#ifdef HAVE_PNG_H
  static char suf[] = ".%04d.png";
#elif unix
  static char suf[] = ".%04d.ppm.gz";
#else /* WIN32 */
  static char suf[] = ".%04d.ppm";
  int needsuf;

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

#ifdef WIN32
typedef unsigned char  boolean;
#endif
extern "C" {
#include <jpeglib.h>
};

static struct jpeg_compress_struct cinfo;
static struct jpeg_error_mgr jerr;


struct my_error_mgr {
  struct jpeg_error_mgr pub;    /* "public" fields */

  jmp_buf setjmp_buffer;        /* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

static struct my_error_mgr myjerr;

static void snapjpegerr(j_common_ptr cinfo)
{
  my_error_ptr myerr = reinterpret_cast<my_error_ptr>(cinfo->err);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}


static int snapjpeg( char *outfname, int xsize, int ysize, char *rgbbuf )
{
    FILE *outf = fopen(outfname, "wb");
    if(outf == 0) {
	msg("Can't open output image %s", outfname);
	return 1;
    }

    cinfo.err = jpeg_std_error(reinterpret_cast<jpeg_error_mgr*>(&myjerr));
    jpeg_create_compress(&cinfo);

    cinfo.image_width = xsize;      /* image width and height, in pixels */
    cinfo.image_height = ysize;
    cinfo.input_components = 3;     /* # of color components per pixel */
    cinfo.in_color_space = JCS_RGB; /* colorspace of input image */

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, ppui.jpegqual, TRUE/*force_baseline*/);

    cinfo.err = jpeg_std_error(&myjerr.pub);
    myjerr.pub.error_exit = snapjpegerr;

    /* Establish the setjmp return context for my_error_exit to use. */
    if (setjmp(myjerr.setjmp_buffer)) {
	/* If we get here, the JPEG code has signaled an error.
	 * We need to clean up the JPEG object, close the input file, and return.
	 */

	/* Display the error message too. */
	(*cinfo.err->output_message) (reinterpret_cast<jpeg_common_struct*>(&cinfo));

	jpeg_destroy_compress(&cinfo);
	fclose(outf);
	return 1;
    }

    jpeg_stdio_dest(&cinfo, outf);

    jpeg_start_compress(&cinfo, TRUE);

    while(cinfo.next_scanline < cinfo.image_height) {
	JSAMPROW rowptr[1];
	rowptr[0] = reinterpret_cast<JSAMPROW>(&rgbbuf[(cinfo.image_height - 1 - cinfo.next_scanline) * xsize*3]);
	jpeg_write_scanlines(&cinfo, rowptr, 1);
    }
    jpeg_finish_compress(&cinfo);
    
    int iook;
    iook = fclose(outf);

    jpeg_destroy_compress(&cinfo);

    return iook ? 1 : 0;	/* return 1 if fclose() reported error, 0 if success */
}
#endif /*HAVE_JPEGLIB_H*/


#ifdef HAVE_PNG_H

/* png image snapshotting */
static png_structp png_ptr = NULL;
static png_infop info_ptr = NULL;

static void failpng() {
    png_destroy_write_struct(&png_ptr, &info_ptr);
    msg("snapshot: error writing png header");
}

/* returns 0 on success, nonzero on failure */
static int snappng( char *outfname, int xsize, int ysize, char *rgbbuf )
{
    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (png_ptr)
	info_ptr = png_create_info_struct(png_ptr);
    if(!info_ptr) {
	msg("snapshot: can't init png library");
	return 1;
    }

    if(setjmp(png_jmpbuf(png_ptr))) {
	failpng();
	return 1;
    }

    FILE *outf = fopen( outfname, "wb" );
    if(outf == 0) {
	return 1;
    }

    png_init_io( png_ptr, outf );

    png_set_IHDR( png_ptr, info_ptr,
		xsize, ysize, 8,/*bit depth*/
		PNG_COLOR_TYPE_RGB,
		PNG_INTERLACE_NONE,
		PNG_COMPRESSION_TYPE_DEFAULT,
		PNG_FILTER_TYPE_DEFAULT);

    /* Don't really know that we're in sRGB color space, but let's say so anyway. */
    // png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_INFO_sRGB);

    png_write_info( png_ptr, info_ptr );

    png_bytep *rowps = new png_bytep[ysize];
    for(int k = 0; k < ysize; k++)
slevy's avatar
slevy committed
	rowps[ysize-k-1] = (png_bytep) &rgbbuf[k*xsize*3];
    png_write_image( png_ptr, rowps );

    png_write_end(png_ptr, info_ptr);
    png_destroy_write_struct(&png_ptr, &info_ptr);
    fclose(outf);
    return 0;
}

#endif /* HAVE_PNG_H */


int parti_snapshot( char *snapinfo, int room )
  char tfcmd1[10240], tfcmd2[10240], *tftail;
  int fail;
  enum imtype { AS_PNG, AS_JPEG, AS_OTHER } astype = AS_OTHER;

#if defined(HAVE_PNG_H) || !WIN32
  static char defsnap[] = "snap.%04d.png";
#else
  static char defsnap[] = "snap.%04d.ppm";
#endif

#if !WIN32  /* if unix */
  static char prefix[] = "|convert ppm:- ";
  static char gzprefix[] = "|gzip >";
#else
  static char prefix[] = "";
#endif

  if(snapinfo) snapinfo[0] = '\0';
  if(ppui.snapfmt == NULL) {
    ppui.snapfmt = NewN(char, sizeof(defsnap)+1);
    strcpy(ppui.snapfmt, defsnap);
  }
  
  if(ppui.snapfmt[0] == '|' || endswith(ppui.snapfmt, ".ppm")) {

#ifdef HAVE_PNG_H
  } else if(endswith(ppui.snapfmt, ".png")) {
    /* use built-in libpng writer if available */
#endif

#ifdef HAVE_JPEGLIB_H
  } else if(endswith(ppui.snapfmt, ".jpeg") || endswith(ppui.snapfmt, ".jpg")) {
    /* use built-in libjpeg writer if available */
    astype = AS_JPEG;
  } else if(endswith(ppui.snapfmt, ".ppm.gz")) {
  snprintf(tftail, tftail+sizeof(tfcmd1)-tftail, ppui.snapfmt, ppui.snapfno);
  if(!ppui.view || !ppui.view->visible_r()) {
    msg("snapshot: no visible graphics window?");
    return -2;
  }

  Fl_Widget *pa;
  for(pa = ppui.view; pa->parent(); pa = pa->parent())
    ;
  pa->show();	// raise window

  bool snapstereo = (strchr(tfcmd1, '@') != NULL);
  enum Gv_Stereo stereowas = ppui.view->stereo();
  char *tfcmd = snapstereo ? tfcmd2 : tfcmd1;

  int y, h = ppui.view->h(), w = ppui.view->w();
  char *buf = (char *)malloc(w*h*3);

  for(int eye = 0; eye < (snapstereo ? 2 : 1); eye++) {
    char eyech = 'L';
    if(snapstereo) {
	ppui.view->stereo( eye==0 ? GV_LEFTEYE : GV_RIGHTEYE );
	strcpy(tfcmd, tfcmd1);
	// replace all instances of "@" with "L" or "R"
	char eyech = eye==0 ? 'L' : 'R';
	for(char *s = tfcmd; (s = strchr(s, '@')) != NULL; s++)
	    *s = eyech;
    }

    // Ensure window's image is up-to-date
    parti_update();

    if(!ppui.view->snapshot( 0, 0, w, h, buf )) {
	free(buf);
	msg("snapshot: couldn't read from graphics window?");
	fail = -2;
	break;
    }

    switch(astype) {
    case AS_PNG:
	fail = snappng( tfcmd, w, h, buf );
    case AS_JPEG:
	fail = snapjpeg( tfcmd, w, h, buf );
	break;
    default:
	 /* write ppm stream/file */
	FILE *p;
	void (*oldpipe)(int) = 0;
	int popened = tfcmd[0] == '|';
	if(popened) {
	    oldpipe = signal(SIGPIPE, SIG_IGN);
	    p = popen(tfcmd+1, "w");
	} else {
	    p = fopen(tfcmd, "wb");
	}
	fprintf(p, "P6\n%d %d\n255\n", w, h);
	for(y = h; --y >= 0 && fwrite(&buf[w*3*y], w*3, 1, p) > 0; )
	    ;
	fflush(p);
	fail = ferror(p) || y >= 0;
	if(popened) {
	    pclose(p);
	    signal(SIGPIPE, oldpipe);
	}
	else
	    fclose(p);
	fprintf(p, "P6\n%d %d\n255\n", w, h);
	for(y = h; --y >= 0 && fwrite(&buf[w*3*y], w*3, 1, p) > 0; )
	    ;
	fflush(p);
	fail = ferror(p) || y >= 0;
	fclose(p);
#endif
	/* end of "write ppm" case */
	break;
    }

    if(fail) {
	msg("snapshot: Error writing to %s", tfcmd);
	break;
    }
slevy's avatar
slevy committed
  free(buf);
  if(snapstereo)	// restore changed stereo setting
    ppui.view->stereo( stereowas );
	snprintf(snapinfo, room, "failed snapping %.900s", tftail);
	snprintf(snapinfo, room, "%.1000s [%dx%d] @ => stereo L,R", tftail, w, h);
	snprintf(snapinfo, room, "%.1000s [%dx%d]", tftail, w, h);
const char *parti_get_alias( struct stuff *st ) {
  return st && st->alias ? st->alias : "";
}

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

  if(newst)
    *newst = ppui.st;

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

	((Fl_Menu_Item *)ppui.obj->menu())[me].labelcolor( ppui.obj->labelcolor() );
	if(!gname)
	    parti_set_alias(stuffs[i], (char *)objname);
    }
    ppui.st = stuffs[i];
    ppui.view->target( i );
    ppui_refresh( ppui.st );
    if(newst) *newst = stuffs[i];
    return i;
  }
  msg("Don't understand object name \"%s\"", objname);
  return OBJNO_NONE;
int parti_idof( struct stuff *st ) {
  for(int i = 0; i < MAXSTUFF; i++)
    if(stuffs[i] == st) return i;
  return OBJNO_NONE;
float parti_focal( CONST char *newfocal ) {
  float focallen;
  if(newfocal && sscanf(newfocal, "%f", &focallen) > 0)
    ppui.view->focallen( focallen );
  return ppui.view->focallen();
}

int parti_focalpoint( int argc, char **argv, Point *focalpoint, float *minfocallen )
slevy's avatar
slevy committed
{
    int ison;
    Point fpt;
    float mlen;
    int changed = 0;

    ison = ppui.view->focalpoint( &fpt, &mlen );

    if(argc > 0) {
	if(isalpha(argv[0][0])) {
	    int nowon = getbool(argv[0], -1);
	    if(nowon < 0)
		argc = 0;
	    else {
		ison = nowon;
		changed = 1;
		argc--;
		argv++;
	    }
	}
	if(getfloats( &fpt.x[0], 3, 0, argc, argv ) ||
		getfloats( &mlen, 1, 3, argc, argv ) ||
		changed)