Skip to content
Snippets Groups Projects
Plot.cc 9.24 KiB
Newer Older
  • Learn to ignore specific revisions
  • #ifdef WIN32
    # include "winjunk.h"
    #endif
    
    /*
     * FLTK OpenGL 2-D plot widget.
     * 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.
    
    #if unix && !__APPLE__
    
    slevy's avatar
    slevy committed
    #include <string.h>
    
    slevy's avatar
    slevy committed
    #include <math.h>
    
    slevy's avatar
    slevy committed
    #include "Plot.H"
    #include "sfont.h"
    #include "shmem.h"
    
    int Plot_add_drawer( Fl_Plot *plot,
    			void (*func)( Fl_Plot *, void *obj, void *arg ),
    			void *obj, void *arg, char *name, int id ) {
    
      return plot->add_drawer( func, obj, arg, name, id );
    
    slevy's avatar
    slevy committed
    }
    
    int Plot_inpick( Fl_Plot *plot ) { return plot->inpick(); }
    
    void Plot_setpick( Fl_Plot *, void (*pickcb)(Fl_Plot *, int hits, int ents, GLuint *buf) );
    
    
    Fl_Plot::Fl_Plot(int x, int y, int w, int h, const char *label)
    
    slevy's avatar
    slevy committed
    		: Fl_Gl_Window(x,y,w,h,label) {
      init();
      end();
    }
    
    
    int Fl_Plot::add_drawer( void (*func)( Fl_Plot *, void *obj, void *arg ),
    				void *obj, void *arg, const char *name, int id )
    {
      if(id < 0) id = next_id();
      int dno = withid( id );
      if(dno < 0) {
        if(ndrawers_ >= maxdrawers_) {
    	maxdrawers_ = ndrawers_*2 + 15;
    	int room = maxdrawers_ * sizeof(struct drawer);
    	drawers_ = (struct drawer *)
    		    (drawers_==NULL ? malloc(room) : realloc(drawers_, room));
        }
        dno = ndrawers_++;
      }
      struct drawer *dp = &drawers_[dno];
      dp->func = func;
      dp->obj = obj;
      dp->arg = arg;
      dp->name = shmstrdup(name);
      dp->id = id;
      notify();
      redraw();
      return id;
    }
    
    void Fl_Plot::notify() {
      if(notify_ != NULL)
        (*notify_)( this, notifyarg_ );
    }
    
    
    int Fl_Plot::withid( int id ) const {	// Which drawer[] slot is id in?
      for(int dno = 0; dno < ndrawers_; dno++)
        if(drawers_[dno].id == id)
    	return dno;
      return -1;
    }
    
    int Fl_Plot::next_id() const {
      int id;
      for(id = 1; withid(id) >= 0; id++)
        ;
      return id;
    }
    
    
    slevy's avatar
    slevy committed
    void draw_axis( float v0, float v1, float ybase, float ytick, float htext, char *just, char *title ) {
    
      char ljust[4], rjust[4];
      sprintf(ljust, "%.2sw", just?just:"");
      sprintf(rjust, "%.2se", just?just:"");
    
    slevy's avatar
    slevy committed
      glColor3f( 1,1,1 );
      glBegin( GL_LINES );
      glVertex2f( 0,ybase );
      glVertex2f( 1,ybase );
      glVertex2f( 0,ybase );
      glVertex2f( 0,ytick );
      glVertex2f( 1,ybase );
      glVertex2f( 1,ytick );
      glEnd();
      char lbl[16];
      sprintf(lbl, "%.2g", v0);
      Point at = { 0, ytick, 0 };
    
      sfStrDrawTJ( lbl, htext*.8, &at, NULL, ljust );
    
    slevy's avatar
    slevy committed
    
      sprintf(lbl, "%.2g", v1);
      at.x[0] = 1;
    
      sfStrDrawTJ( lbl, htext*.8, &at, NULL, rjust );
    
    slevy's avatar
    slevy committed
    
      if(title) {
        at.x[0] = .5;
        at.x[1] = ytick + (ytick-ybase);
        sfStrDrawTJ( title, htext, &at, NULL, "c" );
      }
    
    static float coordof( int pixel, int npix, float o0, float o1, float v0, float v1 ) {
      float o = pixel * (o1-o0) / npix + o0;
      return o * (v1-v0) + v0;
    }
    
    
    static float rounded( int prec, float v ) {
    
      sprintf(str, "%.*g", prec, v);
    
      return atof(str);
    }
    
    static float choosezoom( float zoom, int pixel,
    			int npix, float o0, float o1, float *v0p, float *v1p ) {
      float range = (*v1p - *v0p);
      if(range == 0) return 1;		/* unchanged */
      float newv0, newv1, error;
    
      float center = coordof( pixel, npix, o0, o1, *v0p, *v1p );
      for(int prec = 0; prec < 4; prec++) {
    
        newv0 = rounded( prec, center - .5*range/zoom );
        newv1 = rounded( prec, center + .5*range/zoom );
    
        error = 1 - zoom*(newv1 - newv0)/range;
        if(fabs(error) < .1) break;
      }
      if(newv1 == newv0) return 1;		/* degenerate after 4 digits?! */
      *v0p = newv0;
      *v1p = newv1;
      return range / (newv1 - newv0);
    }
    
    float Fl_Plot::zoomview( float zoomfactor, int xev, int yev ) {
      if(zoomfactor == 0) {
        xrange( initx0_, initx1_ );
        yrange( inity0_, inity1_ );
        redraw();
        return 1.0;
      }
      choosezoom( zoomfactor, h()-yev, h(), orthoy0_, orthoy1_, &y0_, &y1_ );
      float got = choosezoom( zoomfactor, xev, w(), orthox0_, orthox1_, &x0_, &x1_ );
    
    #if unix && !__APPLE__
    
      Window xwin = fl_xid(this);
      if(xwin != None)
          XWarpPointer(fl_display, xwin, xwin, 0, 0, 0, 0, 
    	         (int)(w()*(.5 - orthox0_)/(orthox1_ - orthox0_)),
    	         (int)(h()*(orthoy1_ - .5)/(orthoy1_ - orthoy0_)));
    #endif
    
    slevy's avatar
    slevy committed
    void Fl_Plot::draw() {
      /* draw scene */
    
    
      if(!valid() || damage() || inpick()) {
        /* Assume reshaped */
        valid(1);
        glViewport( 0, 0, w(), h() );
      }
    
    
    slevy's avatar
    slevy committed
      glClearDepth( 1.0 );
      glColorMask( 1, 1, 1, 1 );
      glClearColor( bgcolor_.x[0], bgcolor_.x[1], bgcolor_.x[2], 0 );
      glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
    
      glDisable( GL_DEPTH_TEST );
      glDisable( GL_LIGHTING );
      glDisable( GL_TEXTURE_2D );
      glDisable( GL_COLOR_MATERIAL );
    
      glMatrixMode( GL_PROJECTION );
      glLoadIdentity();
    
    slevy's avatar
    slevy committed
      if(inpick()) {
        GLint vp[4] = {0, 0, w(), h()};
        gluPickMatrix( pickx_, picky_, pickwidth_, pickheight_, vp );
      }
    
      glOrtho( orthox0_, orthox1_,  orthoy0_, orthoy1_,  -1, 1 );
    
    slevy's avatar
    slevy committed
      glMatrixMode( GL_MODELVIEW );
    
      glPushMatrix();
    
    slevy's avatar
    slevy committed
      glScalef( x1_ == x0_ ? 1 : 1/(x1_-x0_),
    	    y1_ == y0_ ? 1 : 1/(y1_-y0_), 1 );
      glTranslatef( -x0_, -y0_, 0 );
    
    slevy's avatar
    slevy committed
      for(int i = 0; i < ndrawers_; i++) {
        struct drawer *dp = &drawers_[i];
    
    slevy's avatar
    slevy committed
        if(dp->func != NULL) {
    	if(inpick_) {
    	    glLoadName( dp->id );
    	    glPushName( 0 );
    	}
    
    slevy's avatar
    slevy committed
    	(*dp->func)( this, dp->obj, dp->arg );
    
    slevy's avatar
    slevy committed
    	if(inpick_)
    	    glPopName();
        }
    
    slevy's avatar
    slevy committed
      }
      glPopMatrix();
    
    slevy's avatar
    slevy committed
      /* Draw axes */
    
    
    slevy's avatar
    slevy committed
      draw_axis( x0_, x1_, -.01, -.04, .1, "n", xtitle() );
    
    slevy's avatar
    slevy committed
      glPushMatrix();
      glRotatef( 90, 0,0,1 );
    
    slevy's avatar
    slevy committed
      draw_axis( y0_, y1_, .01, .04, .1, "s", ytitle() );
    
    slevy's avatar
    slevy committed
      glPopMatrix();
    
    
      if(children() > 0)
        Fl_Gl_Window::draw();
    
    slevy's avatar
    slevy committed
    }
    
    int Fl_Plot::handle(int ev) {
    
      if(eventhook) {
        /* Allow clients to pre-screen our events without subclassing */
        switch((*eventhook)(this, ev)) {
        case 1: return 1;	/* pre-screener handled it */
        case -1: return 0;  /* pre-screener commands us to ignore it too */
        default: break;	/* Else just process event normally below */
        }
      }
    
    
      if(Fl_Gl_Window::handle(ev))
        return 1;
    
    
    slevy's avatar
    slevy committed
      switch(ev) {
      case FL_FOCUS:   hasfocus_ = 1; return 1;  // Yes, we want FL_KEYBOARD events
      case FL_UNFOCUS: hasfocus_ = 0; return 1;
    
      case FL_PUSH:
    	{  // Besides navigating, grab keyboard focus if we're clicked-on
    	    if(!hasfocus_) take_focus();
    	}
      case FL_DRAG:
      case FL_RELEASE:
        if(pickcb_ != NULL) {
    
    	dpickx_ = Fl::event_x();
    	dpicky_ = h() - Fl::event_y();
    
    	if(ev == FL_PUSH) {
    	    do_pick( dpickx_, dpicky_ );
    	    pickneeded_ = 0;
    	} else {
    	    if(!pickneeded_) {
    		pickneeded_ = 1;
    		Fl::add_idle( Fl_Plot::idlepick, (void *)this );
    	    }
    	}
    	return 1;
        }
        /* Maybe do right-button pick? */
        return 0;
    
      case FL_ENTER:
        take_focus();
        return 1;
    
      case FL_KEYBOARD:
    
        if(Fl::event_text() == NULL || Fl::event_text()[0] == '\0')
    	return 0;
    
        int c = Fl::event_text()[0];
    
        switch(c) {
        case 'p':
        case 'P':
    		do_pick( Fl::event_x_root() - x_root(),
    			 y_root() + h() - Fl::event_y_root() );
    	      break;
    
    
        case 'V':	zoomview( 1.33, Fl::event_x(), Fl::event_y() ); break;
        case 'v': 	zoomview( 1/1.33, Fl::event_x(), Fl::event_y() ); break;
    
        case 'w':   zoomview( 0 ); break;
    
    slevy's avatar
    slevy committed
        default: c = 0;
        }
    
    
        if(Fl::event_key(FL_Home)) {
    	zoomview( 0 );
    	return 1;
        }
    
    
    slevy's avatar
    slevy committed
        return c==0 ? 0 : 1;
    
      }
      return 0;
    }
    
    
    
    void Fl_Plot::xrange( float x0, float x1 ) {
        initx0_ = x0_ = x0;
        initx1_ = x1_ = x1;
        notify(); redraw();
    }
    void Fl_Plot::yrange( float y0, float y1 ) {
        inity0_ = y0_ = y0;
        inity1_ = y1_ = y1;
        notify(); redraw();
    }
    
    slevy's avatar
    slevy committed
    void Fl_Plot::xtitle( const char *str ) {
    	if(xtitle_) Free(xtitle_);
    	xtitle_ = str ? shmstrdup(str) : NULL;
    	notify();
    	redraw();
    }
    void Fl_Plot::ytitle( const char *str ) {
    	if(ytitle_) Free(ytitle_);
    	ytitle_ = str ? shmstrdup(str) : NULL;
    	notify();
    	redraw();
    }
    
    
    slevy's avatar
    slevy committed
    void Fl_Plot::picksize( float width, float height )
    {
      pickwidth_ = width;
      pickheight_ = height;
    }
    
    void Fl_Plot::pickbuffer( int words, GLuint *buf )
    {
      picknents_ = words;
      pickbuf_ = buf;
    }
    void Fl_Plot::picker( void (*pickcb)(Fl_Gl_Window *, int, int, GLuint *, void *arg), void *arg )
    {
      pickcb_ = pickcb;
      pickarg_ = arg;
    } 
    
    int Fl_Plot::pickresults( int *nentp, GLuint **bufp )
    {
      if(nentp) *nentp = picknents_;
      if(bufp) *bufp = pickbuf_;
      return pickhits_;
    }
    
    slevy's avatar
    slevy committed
    int Fl_Plot::do_pick( float xpick, float ypick )
    {
      make_current();
      glSelectBuffer( picknents_, pickbuf_ );
      int ok = glRenderMode( GL_SELECT );
      if(ok != 0) {
        fprintf(stderr, "Trouble in do_pick: glRenderMode( GL_SELECT ) = %d\n", ok);
        ok = glRenderMode( GL_SELECT );
        fprintf(stderr, "Retry: %d\n", ok);
      }
      glPushName(0);
    
      inpick_ = 1;
      pickx_ = xpick;
      picky_ = ypick;
      draw();
      inpick_ = 0;
    
      glPopName();
      pickhits_ = glRenderMode( GL_RENDER );
      if(pickcb_)
        (*pickcb_)( this, pickhits_, picknents_, pickbuf_, pickarg_ );
      valid(0);
      return pickhits_;
    }
    
    int Fl_Plot::snapshot( int x, int y, int w, int h,  void *packedrgb )
    {
      make_current();
      glPixelStorei( GL_PACK_ALIGNMENT, 1 );
      glReadBuffer( GL_FRONT );
      glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, packedrgb);
      return 1; // Might return whether this window was properly uncovered?
    }
    
    slevy's avatar
    slevy committed
        // Take snapshot into caller-supplied buffer, w*h*3 bytes long
    
    void Fl_Plot::idlepick( void *vthis ) {
      Fl_Plot *me = (Fl_Plot *)vthis;
    
      if(me->pickneeded_) {
        me->do_pick( me->dpickx_, me->dpicky_ );
        me->pickneeded_ = 0;
        Fl::remove_idle( Fl_Plot::idlepick, vthis );
      }
    }