LoLaserWindow.C

Go to the documentation of this file.
00001 /**
00002    \file Robots/LoBot/ui/LoLaserWindow.C
00003 
00004    This file defines the non-inline member functions of the
00005    lobot::LaseWindow class used to encapsulate the GLUT-based window for
00006    the Hokuyo laser range finder's test program, which visualizes the
00007    laser range finder's measurement data with some simple 2D OpenGL.
00008 */
00009 
00010 // //////////////////////////////////////////////////////////////////// //
00011 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005   //
00012 // by the University of Southern California (USC) and the iLab at USC.  //
00013 // See http://iLab.usc.edu for information about this project.          //
00014 // //////////////////////////////////////////////////////////////////// //
00015 // Major portions of the iLab Neuromorphic Vision Toolkit are protected //
00016 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency //
00017 // in Visual Environments, and Applications'' by Christof Koch and      //
00018 // Laurent Itti, California Institute of Technology, 2001 (patent       //
00019 // pending; application number 09/912,225 filed July 23, 2001; see      //
00020 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status).     //
00021 // //////////////////////////////////////////////////////////////////// //
00022 // This file is part of the iLab Neuromorphic Vision C++ Toolkit.       //
00023 //                                                                      //
00024 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can   //
00025 // redistribute it and/or modify it under the terms of the GNU General  //
00026 // Public License as published by the Free Software Foundation; either  //
00027 // version 2 of the License, or (at your option) any later version.     //
00028 //                                                                      //
00029 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope  //
00030 // that it will be useful, but WITHOUT ANY WARRANTY; without even the   //
00031 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      //
00032 // PURPOSE.  See the GNU General Public License for more details.       //
00033 //                                                                      //
00034 // You should have received a copy of the GNU General Public License    //
00035 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write   //
00036 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,   //
00037 // Boston, MA 02111-1307 USA.                                           //
00038 // //////////////////////////////////////////////////////////////////// //
00039 //
00040 // Primary maintainer for this file: Manu Viswanathan <mviswana at usc dot edu>
00041 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Robots/LoBot/ui/LoLaserWindow.C $
00042 // $Id: LoLaserWindow.C 13037 2010-03-23 01:00:53Z mviswana $
00043 //
00044 
00045 //---------------------- ALTERNATIVE DEFINITION -------------------------
00046 
00047 // In case OpenGL and/or GLUT are missing
00048 //
00049 // NOTE: Don't really need to check INVT_HAVE_LIBGL and INVT_HAVE_LIBGLU
00050 // as well because it ought to be a pretty rare/broken installation that
00051 // has GLUT but not the OpenGL libraries...
00052 #ifndef INVT_HAVE_LIBGLUT
00053 
00054 #include "Robots/LoBot/ui/LoLaserWindow.H"
00055 #include "Robots/LoBot/misc/LoExcept.H"
00056 
00057 namespace lobot {
00058 
00059 void LaserWindow::create(const std::string&)
00060 {
00061    throw missing_libs(MISSING_OPENGL) ;
00062 }
00063 
00064 // NOTE: Don't need empty definitions of the remaining member functions
00065 // because they don't get used at all if OpenGL and/or GLUT are missing,
00066 // which means that the linker won't complain about missing functions
00067 // (since the compiler doesn't generate code for these functions).
00068 
00069 }
00070 
00071 #else // OpenGL and GLUT available ==> the real McCoy
00072 
00073 //------------------------------ HEADERS --------------------------------
00074 
00075 // lobot headers
00076 #include "Robots/LoBot/ui/LoLaserWindow.H"
00077 #include "Robots/LoBot/config/LoConfigHelpers.H"
00078 
00079 #include "Robots/LoBot/misc/LoTypes.H"
00080 #include "Robots/LoBot/misc/factory.hh"
00081 #include "Robots/LoBot/util/LoMath.H"
00082 
00083 // OpenGL headers
00084 #include <GL/glut.h>
00085 
00086 //----------------------------- NAMESPACE -------------------------------
00087 
00088 namespace lobot {
00089 
00090 //------------------------ STATIC DATA MEMBERS --------------------------
00091 
00092 // The window title of the GLUT window created by this class.
00093 std::string LaserWindow::m_title ; // static to allow passage to def. ctor
00094 
00095 //-------------------------- INITIALIZATION -----------------------------
00096 
00097 // This is the only public method in this class. It is static and merely
00098 // acts as an interface for creating the singleton LaserWindow object.
00099 void LaserWindow::create(const std::string& title)
00100 {
00101    m_title = title ;
00102    instance() ;
00103 }
00104 
00105 // Constructor sets up the laser range finder device and GLUT UI
00106 LaserWindow::LaserWindow()
00107    : m_lrf(new LaserRangeFinder(Params::device(), Params::baud_rate())),
00108      m_window(glutCreateWindow(m_title.c_str())),
00109      m_canvas(new GLCanvas()),
00110      m_markings(factory<LaserWindowMarkings>::create(Params::markings_type())),
00111      m_paused(false),
00112      m_drag_button(-1),
00113      m_drag_modifiers(-1)
00114 {
00115    const int M = m_lrf->get_distance_range().max() ;
00116    m_canvas->set_window(-M, M, -M, M) ;
00117 
00118    m_markings->use_canvas(m_canvas) ;
00119    m_markings->set_maximum(M) ;
00120 
00121    glutReshapeFunc(reshape_callback) ;
00122    glutDisplayFunc(render_callback) ;
00123    glutKeyboardFunc(keyboard_callback) ;
00124    glutMouseFunc(click_callback) ;
00125    glutMotionFunc(drag_callback) ;
00126    setup_timer() ;
00127 
00128    typedef LaserWindow me ; // typing shortcut
00129    m_keymap['r'] = & me::reset_zoom_pan ;
00130    m_keymap['p'] = & me::pause ;
00131 
00132    m_keymap['q'] = & me::quit ;
00133    m_keymap['Q'] = & me::quit ;
00134    m_keymap[27]  = & me::quit ; // ESC (assuming ASCII encoding)
00135 
00136    m_drag_prev[0] = m_drag_prev[1] = -1 ;
00137 }
00138 
00139 // This method resets the graphics viewport whenever the UI window is
00140 // resized.
00141 void LaserWindow::reshape(int W, int H)
00142 {
00143    m_canvas->set_viewport(0, W, 0, H) ;
00144 }
00145 
00146 //------------------ UPDATING DISTANCE MEASUREMENTS ---------------------
00147 
00148 void LaserWindow::update()
00149 {
00150    m_lrf->update() ;
00151    glutPostRedisplay() ;
00152    if (! m_paused)
00153       setup_timer() ;
00154 }
00155 
00156 void LaserWindow::setup_timer()
00157 {
00158    glutTimerFunc(Params::update_frequency(), timer_callback, 0) ;
00159 }
00160 
00161 //------------------ RENDERING DISTANCE MEASUREMENTS --------------------
00162 
00163 // Forward declarations
00164 void draw_measurements(const LaserRangeFinder*, const range<int>&, int,
00165                        const GLColor&) ;
00166 void draw_lrf(float, const GLColor&) ;
00167 
00168 // This method draws the latest set of distance measurements from the
00169 // laser range finder.
00170 void LaserWindow::render()
00171 {
00172    glClear(GL_COLOR_BUFFER_BIT) ;
00173 
00174    glPushMatrix() ;
00175       glRotatef(Params::lrf_direction(), 0, 0, 1) ;
00176       m_markings->render() ;
00177       draw_measurements(m_lrf, Params::angles_range(), Params::angles_step(),
00178                         Params::measurements_color()) ;
00179       draw_lrf(Params::lrf_size(), Params::lrf_color()) ;
00180    glPopMatrix() ;
00181 
00182    glutSwapBuffers() ;
00183 }
00184 
00185 // This function draws the measurements made by the laser range finder,
00186 // showing them as rays emanating from the origin of the world coordinate
00187 // system (where the laser range finder is positioned; yes, the laser
00188 // range finder is the center of the world).
00189 //
00190 // Since drawing each and every measurement can make the resulting
00191 // picture crowded, this function only draws the distance measurements
00192 // corresponding to angles within the [min, max] range with the specified
00193 // step size.
00194 void draw_measurements(const LaserRangeFinder* lrf,
00195                        const range<int>& angles, int step,
00196                        const GLColor& color)
00197 {
00198    glPushAttrib(GL_COLOR_BUFFER_BIT) ;
00199    glBegin(GL_LINES) ;
00200       glColor3fv(color.rgb()) ;
00201 
00202       for (float angle = angles.min(); angle <= angles.max(); angle += step)
00203       {
00204          int D = lrf->get_distance(static_cast<int>(angle)) ;
00205          if (D < 0) // didn't get a valid reading in this direction
00206             continue ;
00207          glVertex2i(0, 0) ;
00208          glVertex2f(D * cos(angle), D * sin(angle)) ;
00209       }
00210 
00211       // In case the above loop missed zero degrees (i.e., straight in
00212       // front of the laser range finder...
00213       int D = lrf->get_distance(0) ;
00214       if (D > 0) {
00215          glVertex2i(0, 0) ;
00216          glVertex2i(0, D) ;
00217       }
00218    glEnd() ;
00219    glPopAttrib() ;
00220 }
00221 
00222 // The laser range finder is depicted as a rectangle with a triangle on
00223 // it serving to let users know where the front of the device is. This
00224 // function expects to be passed the half-size R of a square inside of
00225 // which the entire rectangle + triangle combo is to inscribed. The
00226 // rectangle is drawn with sides R and 2R; the triangle is drawn with
00227 // height R and base length 2R.
00228 void draw_lrf(float R, const GLColor& color)
00229 {
00230    glPushAttrib(GL_COLOR_BUFFER_BIT) ;
00231    glBegin(GL_TRIANGLES) ;
00232       glColor3fv(color.rgb()) ;
00233 
00234       // The triangle
00235       glVertex2f(R,  0) ; // apex
00236       glVertex2f(0,  R) ; // base
00237       glVertex2f(0, -R) ;
00238 
00239       // The rectangle (drawn as two triangles)
00240       glVertex2f( 0,  R) ;
00241       glVertex2f(-R,  R) ;
00242       glVertex2f( 0, -R) ;
00243       glVertex2f(-R,  R) ;
00244       glVertex2f(-R, -R) ;
00245       glVertex2f( 0, -R) ;
00246    glEnd() ;
00247    glPopAttrib() ;
00248 }
00249 
00250 //-------------------------- KEYBOARD INPUT -----------------------------
00251 
00252 // Use keymap to invoke appropriate handler for key pressed by user
00253 void LaserWindow::handle_key(unsigned char key)
00254 {
00255    KeyMap::iterator handler = m_keymap.find(key) ;
00256    if (handler == m_keymap.end())
00257       return ;
00258    (this->*(handler->second))() ;
00259    glutPostRedisplay() ;
00260 }
00261 
00262 void LaserWindow::reset_zoom_pan()
00263 {
00264    m_canvas->reset_zoom_pan() ;
00265 }
00266 
00267 void LaserWindow::pause()
00268 {
00269    m_paused = ! m_paused ;
00270    if (! m_paused)
00271       setup_timer() ;
00272 }
00273 
00274 void LaserWindow::quit()
00275 {
00276    exit(0) ;
00277 }
00278 
00279 //---------------------------- MOUSE INPUT ------------------------------
00280 
00281 void LaserWindow::left_click(int state, int modifiers, int x, int y)
00282 {
00283    switch (state)
00284    {
00285       case GLUT_DOWN:
00286          m_drag_button    = GLUT_LEFT_BUTTON ;
00287          m_drag_modifiers = modifiers ;
00288          m_drag_prev[0]   = x ;
00289          m_drag_prev[1]   = y ;
00290          break ;
00291       case GLUT_UP:
00292          m_drag_button    = -1 ;
00293          m_drag_modifiers = -1 ;
00294          m_drag_prev[0]   = -1 ;
00295          m_drag_prev[1]   = -1 ;
00296          break ;
00297    }
00298 }
00299 
00300 void LaserWindow::middle_click(int state, int modifiers, int x, int y)
00301 {
00302    switch (state)
00303    {
00304       case GLUT_DOWN:
00305          m_drag_button    = GLUT_MIDDLE_BUTTON ;
00306          m_drag_modifiers = modifiers ;
00307          m_drag_prev[0]   = x ;
00308          m_drag_prev[1]   = y ;
00309          break ;
00310       case GLUT_UP:
00311          m_drag_button    = -1 ;
00312          m_drag_modifiers = -1 ;
00313          m_drag_prev[0]   = -1 ;
00314          m_drag_prev[1]   = -1 ;
00315          break ;
00316    }
00317 }
00318 
00319 void LaserWindow::right_click(int state, int modifiers, int x, int y)
00320 {
00321    switch (state)
00322    {
00323       case GLUT_DOWN:
00324          m_drag_button    = GLUT_RIGHT_BUTTON ;
00325          m_drag_modifiers = modifiers ;
00326          m_drag_prev[0]   = x ;
00327          m_drag_prev[1]   = y ;
00328          break ;
00329       case GLUT_UP:
00330          m_drag_button    = -1 ;
00331          m_drag_modifiers = -1 ;
00332          m_drag_prev[0]   = -1 ;
00333          m_drag_prev[1]   = -1 ;
00334          break ;
00335    }
00336 }
00337 
00338 void LaserWindow::left_drag(int x, int y)
00339 {
00340    if (m_drag_modifiers & GLUT_ACTIVE_SHIFT) // zoom
00341    {
00342       const float dy = y - m_drag_prev[1] ;
00343       m_canvas->zoom_by(-dy * Params::zoom_drag_factor()) ;
00344    }
00345    else // pan
00346    {
00347       double curr_x, curr_y ;
00348       m_canvas->screen_to_world(x, y, & curr_x, & curr_y) ;
00349 
00350       double prev_x, prev_y ;
00351       m_canvas->screen_to_world(m_drag_prev[0], m_drag_prev[1],
00352                                 & prev_x, & prev_y) ;
00353 
00354       const float dx = static_cast<float>(curr_x - prev_x) ;
00355       const float dy = static_cast<float>(curr_y - prev_y) ;
00356       m_canvas->pan(-dx, -dy) ;
00357    }
00358 
00359    m_drag_prev[0] = x ;
00360    m_drag_prev[1] = y ;
00361 
00362    glutPostRedisplay() ;
00363 }
00364 
00365 void LaserWindow::middle_drag(int x, int y)
00366 {
00367    const float dy = y - m_drag_prev[1] ;
00368    m_canvas->zoom_by(-dy * Params::zoom_drag_factor()) ;
00369 
00370    m_drag_prev[0] = x ;
00371    m_drag_prev[1] = y ;
00372 
00373    glutPostRedisplay() ;
00374 }
00375 
00376 void LaserWindow::right_drag(int, int)
00377 {
00378 }
00379 
00380 //-------------------------- GLUT CALLBACKS -----------------------------
00381 
00382 void LaserWindow::reshape_callback(int width, int height)
00383 {
00384    instance().reshape(width, height) ;
00385 }
00386 
00387 void LaserWindow::render_callback()
00388 {
00389    instance().render() ;
00390 }
00391 
00392 void LaserWindow::keyboard_callback(unsigned char key, int, int)
00393 {
00394    instance().handle_key(key) ;
00395 }
00396 
00397 void LaserWindow::click_callback(int button, int state, int x, int y)
00398 {
00399    switch (button)
00400    {
00401       case GLUT_LEFT_BUTTON:
00402          instance().left_click(state, glutGetModifiers(), x, y) ;
00403          break ;
00404       case GLUT_MIDDLE_BUTTON:
00405          instance().middle_click(state, glutGetModifiers(), x, y) ;
00406          break ;
00407       case GLUT_RIGHT_BUTTON:
00408          instance().right_click(state, glutGetModifiers(), x, y) ;
00409          break ;
00410    }
00411 }
00412 
00413 void LaserWindow::drag_callback(int x, int y)
00414 {
00415    switch (instance().m_drag_button)
00416    {
00417       case GLUT_LEFT_BUTTON:
00418          instance().left_drag(x, y) ;
00419          break ;
00420       case GLUT_MIDDLE_BUTTON:
00421          instance().middle_drag(x, y) ;
00422          break ;
00423       case GLUT_RIGHT_BUTTON:
00424          instance().right_drag(x, y) ;
00425          break ;
00426    }
00427 }
00428 
00429 // We use a timer to continuously update the laser range finder
00430 void LaserWindow::timer_callback(int)
00431 {
00432    instance().update() ;
00433 }
00434 
00435 //----------------------------- CLEAN-UP --------------------------------
00436 
00437 LaserWindow::~LaserWindow()
00438 {
00439    delete m_markings ;
00440    delete m_canvas ;
00441    glutDestroyWindow(m_window) ;
00442    delete m_lrf ;
00443 }
00444 
00445 //-------------------------- KNOB TWIDDLING -----------------------------
00446 
00447 // Parameters initialization
00448 LaserWindow::Params::Params()
00449    : m_device(get_conf<std::string>("device", "port", "/dev/ttyACM0")),
00450      m_baud_rate(get_conf("device", "baud_rate", 115200)),
00451      m_markings_type(get_conf<std::string>("markings", "type", "rings")),
00452      m_update_frequency(clamp(get_conf("device", "update_frequency", 250),
00453                               100, 60000)),
00454      m_angles_range(get_conf<int>("measurements", "angles_range",
00455                                   make_range(-135, 135))),
00456      m_angles_step(clamp(get_conf("measurements", "angles_step", 5), 1, 30)),
00457      m_measurements_color(get_conf<int>("measurements", "color",
00458                                         make_triple(0, 128, 128))),
00459      m_lrf_size(clamp(get_conf("device", "lrf_size", 100.0f), 10.0f, 250.0f)),
00460      m_lrf_direction(clamp_angle(get_conf("device", "lrf_direction", 90.0f))),
00461      m_lrf_color(get_conf<int>("device", "lrf_color",
00462                                make_triple(242, 13, 26))),
00463      m_zoom_drag_factor(clamp(get_conf("zoom_pan", "zoom_drag_factor", 0.1f),
00464                               0.1f, 2.5f))
00465 {}
00466 
00467 // Parameters clean-up
00468 LaserWindow::Params::~Params(){}
00469 
00470 // Parameters access
00471 const std::string& LaserWindow::Params::device()
00472 {
00473    return instance().m_device ;
00474 }
00475 
00476 int LaserWindow::Params::baud_rate()
00477 {
00478    return instance().m_baud_rate ;
00479 }
00480 
00481 const std::string& LaserWindow::Params::markings_type()
00482 {
00483    return instance().m_markings_type ;
00484 }
00485 
00486 int LaserWindow::Params::update_frequency()
00487 {
00488    return instance().m_update_frequency ;
00489 }
00490 
00491 const range<int>& LaserWindow::Params::angles_range()
00492 {
00493    return instance().m_angles_range ;
00494 }
00495 
00496 int LaserWindow::Params::angles_step()
00497 {
00498    return instance().m_angles_step ;
00499 }
00500 
00501 const GLColor& LaserWindow::Params::measurements_color()
00502 {
00503    return instance().m_measurements_color ;
00504 }
00505 
00506 float LaserWindow::Params::lrf_size()
00507 {
00508    return instance().m_lrf_size ;
00509 }
00510 
00511 float LaserWindow::Params::lrf_direction()
00512 {
00513    return instance().m_lrf_direction ;
00514 }
00515 
00516 const GLColor& LaserWindow::Params::lrf_color()
00517 {
00518    return instance().m_lrf_color ;
00519 }
00520 
00521 float LaserWindow::Params::zoom_drag_factor()
00522 {
00523    return instance().m_zoom_drag_factor ;
00524 }
00525 
00526 //-----------------------------------------------------------------------
00527 
00528 } // end of namespace encapsulating this file's definitions
00529 
00530 #endif // #ifndef INVT_HAVE_LIBGLUT
00531 
00532 /* So things look consistent in everyone's emacs... */
00533 /* Local Variables: */
00534 /* indent-tabs-mode: nil */
00535 /* End: */
Generated on Sun May 8 08:41:31 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3