Main Page | Modules | Namespace List | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

Writing Module Displays

The Module class is fundamentally agnostic when it comes to module displays. It provides and invokes the initializeDisplay, display, and displayIdle methods, but it says nothing about how to implement those for development and debugging display.

While the Module class doesn't enforce a particular brand of display, the ModUtils package does provide libraries to support several types of display, one for 2D displays and one for 3D displays. Both libraries use FLTK, a simple, cross platform GUI toolkit and GL, the standard 3D darwing package, to do their displays. The ModUtils includes the appropriate version of FLTK as part of its installation and assumes you have an appropriate version of GL on your system.

Since both libraries are FLTK based, for both you should set the displayIdle routine to simply check for FLTK events so that your display works well when the module is paused.

bool BinaryObstModule::displayIdle()
{
  Fl::check();
  return true;
}

2D Displays

Many robotic tasks are best served by an overhead view of a 2D world. To support this we supply the Display library, which is included in your Makefile with -lDisplay and supported by the header file include/Display/Display.h.

The Display library creates a named display which is an instance of the class Display. You add callbacks to the display which do the 2D GL drawing, and then you add a Display redraw invocation to your Module display method to actually render your display.

Configuring

You will add a private member variable to hold your Display instance. We suggest you set it to NULL in the module constructure and delete it in the destructor. As an aside, this is a common idiom for optional things, as deletion of NULL is defined in C++ as a no-op, and displays are not always going to be created, i.e., when the display is turned off, the display member variable will stay NULL.

In your module's initializeDisplay method you should do somethinhg like,

const char* spec = config->getString("display_spec");
if (!spec[0]) {
  fprintf(stderr, "No display_spec\n");
  return false;
}
_display = new Display(spec, table);
This should set the private member variable _display to a valid Display instance.

Once you have created the Display instance, you will add your display callbacks using

_display->add_callback(static_callback, priority, frame, this);
where

Then you can invoke any number of Display methods to configure your display. These can include,

_display->size_range(500, 500); // set size to 500x500 pixels
_display->track_veh_pos(true); // means display will follow vehicle position
_display->track_veh_rot(true); // means display will follow vehicle orientation
_display->set_northing_window(50.0); // means the vertical size will be 50m
_display->mouse_zoomable(true);  // means user can zoom with the mouse
_display->mouse_moveable(false); // means user cannot move display with the mouse

Finally, you need a few bits of magic to finish of the initializeDisplay which will actually show the display

_display->set_visible();  // make sure it will be drawn
_display->show();         // make the window shown
_display->redraw();       // force an initial redraw

Callbacks

The callbacks are the meat of your 2D display.

First you must define a status module member function to pass to Display::add_callback. This static member function's only purpose will be to call a non-static member function with the this pointer passed in to the add_callback invocation. You should define the callback functions like this,

private:
  // static member function
  // with lots of things you needn't worry about
  static void map_render_callback(
        double e_o,  double n_o,
        double low_x, double low_y,
        double high_x, double high_y,
        utils::SymbolTable *st, void *data);
  // the real drawing function
  void map_render(double e_o, double n_o);

The static member callback will almost always look something like this:

void BinaryObstModule::map_render_callback(
  // takes easting and northing offsets
  double e_o, double n_o,
  // bounding box
  double low_x, double low_y, double high_x, double high_y, 
  // global symbol table
  utils::SymbolTable *st, 
  // and passed in user data (module pointer)
  void *data)
{
  ((BinaryObstModule *)data)->map_render(e_o, n_o);
}

The non-static member callback, in this case map_render, does the actual drawing. You will use normal GL calls such as glRectd, glBegin(GL_POINTS), glVertex2d, glEnd, etc.

In the DISPLAY_WINDOW frame of reference, the coordinates for any drawing functions are simply the window coordinates, but in the DISPLAY_VEHICLE and DISPLAY_GLOBAL coordinate system you have to remember that x and y are switched, i.e., a global or vehicle x should be passed in as the y coordinate of a GL call and a global or vehicle y should be passed in as the x coordinate of a GL call.

In the DISPLAY_VEHICLE coordinate frame, all drawing is done relative to the vehicle position, which you will be setting using the set_veh_pos routine (as specified in the next section).

In the DISPLAY_VEHICLE coordinates you should subtract the easting offset (e_o in our example) from the y coordinate and the northing offset (n_o in our example) from the x coordinate before passing them into your GL calls. This annoying feature is due to the fact that GL, internally at least on many machines, is fundamentally a 32 bit library, even though it claims to have double precision routines. Thus if your coordinates are really huge numbers, like you might get in UTM's from a GPS system, then the precision of single precision floating point numbers is not sufficient to represent position accurately. Therefor the Display library caches a base northing and easting offset which you use as an offset to get the coordinates into the realm of the precision of a single precision floating point number. If you do not do this, or the northing and easting offset are set incorrectly, you will see the display "jump around" when it should be fixed due to insufficient precision in the position.

The Display Routine

The display routine should be fairly simple. First, you have to update the vehicle position and orientation with the set_veh_pos if you are going to use any callbacks in the DISPLAY_VEHICLE coordinate frame, or if you want the display to track the vehicle motion when the track_veh_pos and track_veh_rot options (see the Display Configuring section). Then you need to tell the display instance to redraw and check for any FLTK events.

The boilerplate for a 2D display display method is something like this,

bool BinaryObstModule::display()
{
  // get the absolute pose of the vehicle
  double northings, eastings, heading;
  get_absolute_vehicle_position(northings, eastings, heading);
  // and tell the display code this so we move the display accordingly
  _display->set_veh_pos(eastings, northings, heading);

  _display->redraw();   // update the display
  Fl::check();   // check for any FLTK events 
  return true; // return success!
}

3D Displays

Often in robotics development you need more than an overhead, 2D map for debugging. We have included a utility library to ease the development of 3D, GL-based displays.

You define and register 3D objects such as ground plane grids and point lists and the GlutDisplay class takes care of rendering these appropriately. You can change your registered objects' parameterizations and those changes will be reflected on the main display. The GLUtils display is normally used for vehicle centered 3D displays, although there is no reason not to use it for global displays.

To use the GLUtils library you must add -lGLUtils to your library list and include the file include/GLUtils/GlutDisplay.h for much of the functionality. In most cases, you will also have to include the header files corresponding to the 3D objects you will be using.

Configuring

After including the appropriate files, you should define the GlutDisplay and necessary 3D objects as member variables
GlutDisplay *_display;  // the 3D point display
GL_PointList * _pts;    // the point list object for 3D display
GL_Origin *_ori;        // the origin for 3D display

In your initializeDisplay method you should create and configure the display

_display = new GlutDisplay();
_display->set_z_down(); // necessary to get z-axis in right direction
Note that in many cases the coordinate system for vehicles uses a convention in which the z axis points down.

Then, in initializeDisplay you create your objects

// Create a fixed origin with 1m arms
 _ori = new GL_Origin(1.0);

// Create a point list, which will actually show our changing data
_pts = new GL_PointList;

And then, register them with the display

_display->register_object(_pts);
_display->register_object(_ori);

Now _display will render your registered objects.

Display Objects

Every display object has some basic operations,

You can get some general ornamentation objects included via include/GLUtils/GlutDisplay.h (although they are actually definhed in include/GLUtils/gl_object.h). These include

Besides these ornamentations, you will most commonly use the GL_PointList class from the GL_PointList.h. This class can be used for a 3D point cloud display by setting points its render_points method, or it can be used for a 3D path by setting points via its render_path method.

Other 3D objects you can use include

Updating Display Objects

Most of the updating of a GlutDisplay display is done behind the scenes, but if you are going to change object values there are some details that must be taken care of.

First, in your module's display method you should change the relevant object values.

// display _points in GL window
_pts->render_points(_points, 1.0, 1.0, 1.0, 1); 

If you just change the object values, the changes will not propagate to the display until a redisplay is necessary, which by default will only happen when there are mouse events or window exposures. To force a redisplay of your new variables you have to manually post a redisplay:

_display->post_redraw();

Generated on Fri Jun 16 13:21:26 2006 for ModUtils by  doxygen 1.4.4