core/vgui/vgui_viewer2D_tableau.cxx
Go to the documentation of this file.
00001 // This is core/vgui/vgui_viewer2D_tableau.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author Philip C. Pritchett, RRG, University of Oxford
00008 // \date   14 Sep 1999
00009 // \brief  See vgui_viewer2D_tableau.h for a description of this file.
00010 
00011 #include "vgui_viewer2D_tableau.h"
00012 
00013 #include <vcl_cmath.h>
00014 #include <vbl/vbl_bool_ostream.h>
00015 
00016 #include <vgui/vgui_gl.h>
00017 #include <vgui/vgui_glu.h>
00018 #include <vgui/vgui.h>
00019 #include <vgui/vgui_event.h>
00020 #include <vgui/vgui_window.h>
00021 #include <vgui/vgui_adaptor.h>
00022 #include <vgui/vgui_event_condition.h>
00023 #include <vgui/vgui_drag_mixin.h>
00024 #include <vgui/vgui_matrix_state.h>
00025 #include <vgui/vgui_find.h>
00026 #include <vgui/vgui_utils.h>
00027 #include <vgui/vgui_image_tableau.h> // for centering
00028 
00029 // to transform from window coordinates (x,y) to image coordinates (ix,iy)
00030 //
00031 //   ix = (x - token.offsetX) / token.scaleX;
00032 //   iy = (y - token.offsetY) / token.scaleY;
00033 
00034 //  h = w * (H/W)
00035 //  w = h * (W/H)
00036 
00037 const void * const vgui_viewer2D_tableau::CENTER_EVENT="x";
00038 
00039 // this is what it always was. please leave it. -- fsm.
00040 vgui_event_condition c_pan(vgui_MIDDLE, vgui_CTRL);
00041 #if 0 // was:
00042 vgui_event_condition c_pan(vgui_LEFT, vgui_modifier(vgui_CTRL + vgui_SHIFT));
00043 #endif // 0
00044 
00045 vgui_viewer2D_tableau::vgui_viewer2D_tableau(vgui_tableau_sptr const& s) :
00046   vgui_wrapper_tableau(s),
00047   nice_points(true),
00048   nice_lines(true),
00049   zoom_type(normal_zoom),
00050   panning(false),
00051   smooth_zooming(false),
00052   sweep_zooming(false),
00053   sweep_next(false),
00054   prev_x(0), prev_y(0),
00055   zoom_x(0), zoom_y(0),
00056   new_x(0), new_y(0),
00057   zoom_factor(1.5f),
00058   npos_x(0), npos_y(0)
00059 {
00060 }
00061 
00062 vgui_viewer2D_tableau::~vgui_viewer2D_tableau()
00063 {
00064 }
00065 
00066 void vgui_viewer2D_tableau::setup_gl_matrices()
00067 {
00068   GLint vp[4];
00069   glGetIntegerv(GL_VIEWPORT, vp);
00070   int width  = vp[2];
00071   int height = vp[3];
00072 
00073   // the projection matrix sets up GL for
00074   // rendering in window coordinates.
00075   glMatrixMode(GL_PROJECTION);
00076   glLoadIdentity();
00077   glOrtho(0, width,  // left, right
00078           height, 0, // bottom, top
00079           -1,+1);    // near, far
00080 
00081   // the modelview matrix applies a transformation
00082   // to incoming coordinates before they reach the
00083   // projection matrix. in this case, it consists
00084   // of anisotropic scaling about (0,0) followed
00085   // by a translation.
00086   glMatrixMode(GL_MODELVIEW);
00087   glLoadIdentity();
00088   glTranslatef(token.offsetX, token.offsetY, 0);
00089   glScalef(token.scaleX, token.scaleY, 1);
00090 }
00091 
00092 
00093 // this routine will modify the token in such a way as to
00094 // effect a zoom about the point (x, y) by the given factor.
00095 // (x, y) are in viewport coordinates.
00096 void vgui_viewer2D_tableau::zoomin(float zoom_fac, int x, int y)
00097 {
00098   // this bit is easy.
00099   token.scaleX *= zoom_fac;
00100   token.scaleY *= zoom_fac;
00101 
00102   // this bit is tricky.
00103   GLint vp[4]; glGetIntegerv(GL_VIEWPORT,vp);
00104   float dx = (        (x-vp[0])) - token.offsetX;
00105   float dy = (vp[3]-1-(y-vp[1])) - token.offsetY;
00106 
00107   float tmpx = zoom_fac*dx - dx;
00108   float tmpy = zoom_fac*dy - dy;
00109 
00110   token.offsetX -= tmpx;
00111   token.offsetY -= tmpy;
00112 }
00113 
00114 
00115 void vgui_viewer2D_tableau::zoomout(float zoom_fac, int x, int y)
00116 {
00117   zoomin(1.0f / zoom_fac, x, y);
00118 }
00119 
00120 void vgui_viewer2D_tableau::center_image(int w, int h)
00121 {
00122   GLfloat vp[4];
00123   glGetFloatv(GL_VIEWPORT, vp);
00124   float width = vp[2];
00125   float height = vp[3];
00126 
00127   token.offsetX =  width/2 - token.scaleX*(float(w)/2.0f);
00128   token.offsetY = height/2 - token.scaleY*(float(h)/2.0f);
00129   post_redraw();
00130 }
00131 
00132 
00133 vcl_string vgui_viewer2D_tableau::type_name() const {return "vgui_viewer2D_tableau";}
00134 
00135 static void draw_rect(float x0, float y0, float x1, float y1)
00136 {
00137   glColor3f(1,0,0);
00138 
00139   glLineWidth(2);
00140   glBegin(GL_LINE_LOOP);
00141   glVertex2f(x0,y0);
00142   glVertex2f(x0,y1);
00143   glVertex2f(x1,y1);
00144   glVertex2f(x1,y0);
00145   glEnd();
00146 }
00147 
00148 bool vgui_viewer2D_tableau::handle(const vgui_event& e)
00149 {
00150   if (e.type == vgui_DRAW)
00151   {
00152     // Setup OpenGL for 2D
00153     glDisable(GL_CULL_FACE);
00154     glDisable(GL_DEPTH_TEST);
00155     glDisable(GL_TEXTURE_2D);
00156     glDisable(GL_LIGHTING);
00157     glShadeModel(GL_FLAT);
00158 
00159     if (nice_points)
00160       glEnable(GL_POINT_SMOOTH);
00161     else
00162       glDisable(GL_POINT_SMOOTH);
00163 
00164     if (nice_lines)
00165     {
00166       glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
00167       glEnable(GL_LINE_SMOOTH);
00168       glLineWidth (1.5);
00169     }
00170     else
00171       glDisable(GL_LINE_SMOOTH);
00172 
00173     if (nice_points || nice_lines)
00174     {
00175       glEnable (GL_BLEND);
00176       glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00177       // glBlendFunc (GL_SRC_ALPHA,  GL_ONE);
00178     }
00179     else
00180       glDisable(GL_BLEND);
00181 
00182     setup_gl_matrices();
00183 
00184     return child->handle(e);
00185   }
00186   // center the scroll bars if the image is centered
00187   // normally this would be in the key press event
00188   // routine, but we need to get our hands on the window
00189   // and the event is not passed into the key_press method.
00190   if (e.type ==vgui_KEY_PRESS)
00191     if (e.key=='c')
00192     {
00193       vgui_adaptor* adap = e.origin;
00194       vgui_window* win = adap->get_window();
00195       if (win)
00196       {
00197         // current scroll pos range is [0,100]
00198         // eventually we would want to get the range
00199         // from the window to insure consistency
00200         int cpos = 50;
00201         win->set_hscrollbar(cpos);
00202         win->set_vscrollbar(cpos);
00203         npos_x = cpos;
00204         npos_y = cpos;
00205       }
00206     }
00207   // We really want to be able to scroll the entire
00208   // image through the viewport.  The token offset should be
00209   // adjusted proportionally to the size of the image
00210   // added routine to get image size JLM
00211   // This deals with horizontal scroll message
00212   if (e.type == vgui_HSCROLL)
00213   {
00214     int w, h;
00215     this->image_size(w,h);
00216     float xs = w/100.0f;
00217     this->token.offsetX -= this->token.scaleX*xs*(*((const int*)e.data)-npos_x);
00218     this->post_redraw();
00219     npos_x = *((const int *)e.data);
00220   }
00221   // This deals with vertical scroll message
00222   if (e.type == vgui_VSCROLL)
00223   {
00224     int w, h;
00225     this->image_size(w, h);
00226     float ys = h/100.0f;
00227     this->token.offsetY -= this->token.scaleY*ys*(*((const int*)e.data)-npos_y);
00228     this->post_redraw();
00229     npos_y = *((const int *)e.data);
00230   }
00231   setup_gl_matrices();
00232 
00233   if (vgui_drag_mixin::handle(e))
00234     return true;
00235 
00236   if (vgui_tableau::handle(e))
00237     return true;
00238 
00239   return child->handle(e);
00240 }
00241 
00242 bool vgui_viewer2D_tableau::mouse_down(int x, int y, vgui_button button, vgui_modifier modifier)
00243 {
00244   // Middle mouse button press.  Update last seen mouse position. And set
00245   //  "panning" true since button is pressed.
00246   if (c_pan(button, modifier))
00247   {
00248 #ifdef DEBUG
00249     vcl_cerr << "vgui_viewer2D_tableau::mouse_down: middle\n";
00250 #endif
00251     prev_x = x;
00252     prev_y = y;
00253     panning = true;
00254     return true;
00255   }
00256   if (this->zoom_type == vgui_viewer2D_tableau::normal_zoom && !sweep_next)
00257   {
00258     if (button == vgui_LEFT && (modifier & vgui_CTRL))
00259     {
00260 #ifdef DEBUG
00261       vcl_cerr << "vgui_viewer2D_tableau::mouse_down: left\n";
00262 #endif
00263       this->zoomin(zoom_factor, int(x), int(y));
00264       this->post_redraw();
00265       return true;
00266     }
00267     else if (button == vgui_RIGHT && (modifier & vgui_CTRL))
00268     {
00269 #ifdef DEBUG
00270       vcl_cerr << "vgui_viewer2D_tableau::mouse_down: right\n";
00271 #endif
00272       this->zoomout(zoom_factor, int(x), int(y));
00273       this->post_redraw();
00274       return true;
00275     }
00276   }
00277   else if (this->zoom_type == vgui_viewer2D_tableau::smooth_zoom && !sweep_next)
00278   {// if this->smooth_zoom
00279     if (button == vgui_LEFT && (modifier & vgui_CTRL))
00280     {
00281 #ifdef DEBUG
00282       vcl_cerr << "vgui_viewer2D_tableau::mouse_down: left\n";
00283 #endif
00284       prev_x = x;
00285       prev_y = y;
00286       zoom_x = x;
00287       zoom_y = y;
00288       smooth_zooming = true;
00289 
00290       return true;
00291     }
00292   }
00293   else if (button == vgui_LEFT && (sweep_next || (modifier & vgui_CTRL)))
00294   {
00295     // this is the beginning of the sweep zoom operation. we have to (a) set the
00296     // state flag 'sweep_zooming', (b) remember the position of the pointer and
00297     // (c) save the front buffer to the back buffer.
00298     prev_x = x;
00299     prev_y = y;
00300     zoom_x = x;
00301     zoom_y = y;
00302     sweep_zooming = true;
00303 #ifdef DEBUG
00304     vcl_cerr << "copy_back_to_front...";
00305 #endif
00306     vgui_utils::copy_back_to_front();
00307 #ifdef DEBUG
00308     vcl_cerr << "done\n";
00309 #endif
00310 
00311     return true;
00312   }
00313 
00314   return false;
00315 }
00316 
00317 bool vgui_viewer2D_tableau::mouse_drag(int x, int y,  vgui_button /*button*/, vgui_modifier /*modifier*/)
00318 {
00319 #ifdef DEBUG
00320   vcl_cerr << __FILE__ ": vgui_viewer2D_tableau_handler::mouse_drag\n";
00321 #endif
00322 
00323   if (!panning && !smooth_zooming && !sweep_zooming)
00324     return false;
00325 
00326   if (panning)
00327   {
00328     // the mouse events come in viewport coordinates, so the relevant
00329     // translation in window coordinates is (dx,-dy).
00330     this->token.offsetX += (x-prev_x);
00331     this->token.offsetY -= (y-prev_y);
00332     this->post_redraw();
00333   }
00334 
00335   if (smooth_zooming)
00336   {
00337     GLdouble vp[4];
00338     glGetDoublev(GL_VIEWPORT, vp);
00339     double height = vp[3];
00340 
00341     float newscale = 1.f - (1.5f*(prev_y - y)/(float)height);
00342 
00343     this->zoomin(newscale, int(zoom_x), int(zoom_y));
00344     this->post_redraw();
00345   }
00346 
00347   if (sweep_zooming)
00348   {
00349     // this is called during the sweep zoom operation. we have
00350     // to (a) repair the front buffer, (b) draw the new rectangle
00351     // and (c) remember where the mouse pointer was (in new_x, new_y).
00352     vgui_matrix_state gl_state;  gl_state.save();
00353 
00354 #ifdef DEBUG
00355     vcl_cerr << "begin_sw_overlay...";
00356 #endif
00357     vgui_utils::begin_sw_overlay();
00358 #ifdef DEBUG
00359     vcl_cerr << "done\n"
00360              << "copy_back_to_front...";
00361 #endif
00362     vgui_utils::copy_back_to_front();
00363 #ifdef DEBUG
00364     vcl_cerr << "done\n";
00365 #endif
00366 
00367     // set projection*modelview matrices to render in **viewport** coordinates :
00368     GLdouble vp[4];
00369     glGetDoublev(GL_VIEWPORT, vp);
00370     double width = vp[2];
00371     double height = vp[3];
00372 
00373     glMatrixMode(GL_PROJECTION);
00374     glLoadIdentity();
00375     gluOrtho2D(0, width, 0, height);
00376 
00377     glMatrixMode(GL_MODELVIEW);
00378     glLoadIdentity();
00379 
00380     // get ratio of viewport width to viewport height :
00381     float W_H_ratio = float(width/height);
00382 
00383     // new method - draws rectangular box of aspect ratio W/H
00384     new_x = x;
00385     new_y = y;
00386 
00387     // compute the size of the sweep area in pixels on the screen :
00388     float xdiff = vcl_fabs(zoom_x - new_x);
00389     float ydiff = vcl_fabs(zoom_y - new_y);
00390 
00391     // this bit here makes sure the swept out region has the
00392     // same shape as the viewport :
00393     if (xdiff > ydiff*W_H_ratio)
00394     {
00395       if ((zoom_y - y) > 0)
00396         new_y = zoom_y - xdiff/W_H_ratio;
00397       else
00398         new_y = zoom_y + xdiff/W_H_ratio;
00399     }
00400     else
00401     {
00402       if ((zoom_x - x) > 0)
00403         new_x = zoom_x - ydiff*W_H_ratio;
00404       else
00405         new_x = zoom_x + ydiff*W_H_ratio;
00406     }
00407 
00408     // useful line as it documents the meaning of
00409     // zoom_? and new_?
00410     draw_rect(zoom_x - float(vp[0]), zoom_y - float(vp[1]), new_x - float(vp[0]), new_y - float(vp[1]));
00411 
00412     vgui_utils::end_sw_overlay();
00413   }
00414 
00415   // Update last seen mouse position.
00416   prev_x = x;
00417   prev_y = y;
00418   return true;
00419 }
00420 
00421 bool vgui_viewer2D_tableau::mouse_up(int /*x*/, int /*y*/,  vgui_button button, vgui_modifier /*modifier*/)
00422 {
00423 #ifdef DEBUG
00424   vcl_cerr << "vgui_viewer2D_tableau_handler::mouse_up\n";
00425 #endif
00426 
00427   if (sweep_zooming && button == vgui_LEFT)
00428   {
00429     // this is the end of the sweepzoom operation.
00430     // we have to deduce and set the new parameters in the token
00431     // from the viewport coordinates (zoom_x, zoom_y) and (new_x, new_y).
00432     // the safe way to do this is convert the sweep region into pre-modelview
00433     // coordinates and then deduce the token parameters from that.
00434     //
00435     // we modify the token to make the sweep region fill the whole viewport.
00436     sweep_zooming = false;
00437     sweep_next = false;
00438 
00439     // get size of viewport
00440     GLfloat vp[4];
00441     glGetFloatv(GL_VIEWPORT, vp);
00442 
00443     // compute pre-modelview coordinates of corners of sweep region :
00444     float x1 = (        (zoom_x-vp[0]) - this->token.offsetX) / this->token.scaleX;
00445     float y1 = (vp[3]-1-(zoom_y-vp[1]) - this->token.offsetY) / this->token.scaleY;
00446     float x2 = (        ( new_x-vp[0]) - this->token.offsetX) / this->token.scaleX;
00447     float y2 = (vp[3]-1-( new_y-vp[1]) - this->token.offsetY) / this->token.scaleY;
00448 
00449     // set the new parameters in the token :
00450     this->token.scaleX = vp[2]/(x2-x1);
00451     this->token.scaleY = vp[3]/(y2-y1);
00452     this->token.offsetX = - this->token.scaleX*x1;
00453     this->token.offsetY = - this->token.scaleY*y1;
00454 
00455     this->post_redraw(); // we probably need one now
00456   }
00457 
00458   if (smooth_zooming && button == vgui_LEFT)
00459     smooth_zooming = false;
00460 
00461   if (panning && button == c_pan.button)
00462     panning = false;
00463 
00464   return false;
00465 }
00466 
00467 bool vgui_viewer2D_tableau::help()
00468 {
00469   vcl_cerr << "\n-- vgui_viewer2D_tableau ----------\n"
00470            << "|     mouse               |\n"
00471            << "| ctrl+left       zoom in |\n"
00472            << "| ctrlt+middle        pan |\n"
00473            << "| ctrl+right     zoom out |\n"
00474            << "|                         |\n"
00475            << "|     keys                |\n"
00476            << "| ctrl+`c'   center image |\n"
00477            << "| ctrl+`x'   resize image |\n"
00478            << "| `-'   lower zoom factor |\n"
00479            << "| `='   raise zoom factor |\n"
00480            << "| `n'     toggle aliasing |\n"
00481            << "| `z'    toggle zoom type |\n"
00482            << "| `d'          sweep zoom |\n"
00483            << "--------------------------\n\n";
00484   return false;
00485 }
00486 
00487 bool vgui_viewer2D_tableau::image_size(int& width, int& height)
00488 {
00489   vgui_tableau_sptr t = vgui_find_below_by_type_name(this,"vgui_image_tableau");
00490   if (!t)
00491     t = vgui_find_below_by_type_name(this, "xcv_image_tableau");
00492   if (t)
00493   {
00494     vgui_image_tableau_sptr im; im.vertical_cast(t);
00495     width = im->width();
00496     height = im->height();
00497     return true;
00498   }
00499   else
00500   {
00501     width = 0; height = 0;
00502     vcl_cerr << __FILE__ " : no image found\n";
00503   }
00504   return false;
00505 }
00506 
00507 void vgui_viewer2D_tableau::center_event()
00508 {
00509   int width=0, height=0;
00510   this->image_size(width, height);
00511   this->center_image(width, height);
00512 }
00513 
00514 bool vgui_viewer2D_tableau::key_press(int /*x*/, int /*y*/, vgui_key key, vgui_modifier modifier)
00515 {
00516 #ifdef DEBUG
00517   vcl_cerr << "vgui_viewer2D_tableau_handler::key_press " << key << '\n';
00518 #endif
00519   if (modifier & vgui_CTRL) vgui::out << "CTRL+" << char(key) << " pressed: CTRL ignored\n";
00520   switch (key)
00521   {
00522    case 'x':
00523     vgui::out << "viewer2D : resizing image\n";
00524     this->token.scaleX = 1;
00525     this->token.scaleY = 1;
00526     center_event();
00527     this->post_redraw();
00528     return true;
00529    case 'c':
00530     vgui::out << "viewer2D : centering image\n";
00531     center_event();
00532     return true;
00533    case '-':
00534     zoom_factor -= 0.1f;
00535     vgui::out << "viewer2D : zoom_factor = " << zoom_factor << '\n';
00536     return true;
00537    case '=':
00538     zoom_factor += 0.1f;
00539     vgui::out << "viewer2D : zoom_factor = " << zoom_factor << '\n';
00540     return true;
00541    case 'n':
00542     this->nice_points = !this->nice_points;
00543     this->nice_lines = !this->nice_lines;
00544     vgui::out << "viewer2D : antialiased points & lines "
00545               << vbl_bool_ostream::on_off(this->nice_points) << '\n';
00546     this->post_redraw();
00547     return true;
00548    case 'd':
00549     sweep_next = true;
00550     return true;
00551    case 'z':
00552     if (this->zoom_type == vgui_viewer2D_tableau::normal_zoom)
00553     {
00554       this->zoom_type = vgui_viewer2D_tableau::smooth_zoom;
00555       vgui::out << "viewer2D : smooth zoom\n";
00556     }
00557     else
00558     {
00559       this->zoom_type = vgui_viewer2D_tableau::normal_zoom;
00560       vgui::out << "viewer2D : normal zoom\n";
00561     }
00562     return true;
00563    default:
00564     return false;
00565   }
00566 }