Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Open Source

The Hot Views Graphics Library


The Hot Views Graphics Library

David is a physicist at the Thomas Jefferson National Accelerator Facility in Newport News, Virginia. He can be contacted at [email protected] or [email protected].


The Hot Views (Hv) graphical user-interface library was designed for use in scientific modeling and simulation applications. It is layered on top of the ubiquitous UNIX-based X, Xt, and Motif libraries. Hv will likely be useful to you if:

  • You are developing a model or simulation of real objects that you would like to display using a (floating-point) world-coordinate system, and/or you need to generate scientific/engineering style graphs of simulation results.
  • Your application requires pointer feedback; when a user points at a representation of an object on the screen, you want to provide instant visual feedback regarding the object's state.
  • You want to be able to print the graphical representation of the simulation, or render it to a Postscript file for import into another document.

These may sound like trivial requirements until you consider that X, Xt, and Motif do not provide such services. They are pixel based, making representations of real objects tedious. Nor do they provide direct information regarding which object contains the pointer. Finally, they do not support the translation of low-level X-based drawing into Postscript.

The world-coordinate systems maintained by Hv are a more substantive feature than you might first suspect. You can easily render floating-point coordinates onto a pixel-based display by simple scaling. However, Hv will automatically handle the arbitrary linear transformations associated with zooming, scrolling, and object rotation. Furthermore, Hv does not indiscriminately transform every object. For example, suppose you placed a "print" button on top of a map. If you zoomed into the map, Hv would transform the borders accordingly but the button would stay the same size. Other Hv features include drag-and-drop, integrated plotting, zooming, time-step simulations, animation, drawing tools, double buffering, virtual desktop, online and balloon help, map drawing, drawing primitives, scrolling, simplified fonts/colors, 3D sculptured look, image menus, and timed redraws.

Hv's drawing is optimized to redraw the minimum amount of a window that needs refreshing. Hv also lets you earmark items for off-screen caching. For example, a war game might cache maps, which are expensive to draw. With Hv, as tokens are dragged over the map, the user doesn't have to wait for the background maps to be recomputed and redrawn.

Hv is comprised of about 60,000 lines of ANSI C code. Applications have been written in C/C++. It has been tested on virtually all flavors of UNIX, including Linux. The Linux version has been tested with commercial Motif libraries as well as the recently available and cleverly named "lesstif freeware Motif clone."

Hv was developed at the Thomas Jefferson National Accelerator Facility, a Department of Energy nuclear physics research laboratory. It is in use at many government and commercial sites worldwide and available via anonymous ftp from ftp.cebaf.gov in /pub/heddle/Hv or at http://www.cebaf.gov/~heddle/Hv. The distribution is replete with demos and a comprehensive programming manual.

The Hv Paradigm

An Hv application is contained in a single X window, called the "main window," with a single menubar, not unlike a Macintosh desktop. Within the main window are additional window-like objects called "views." Views might contain independent simulation displays or may represent different perspectives of a single model. Figure 1 is an example of an Hv main window containing multiple views. This structure was chosen over independent X windows for each view to avoid having to hunt around a cluttered screen for a lost window.

The views behave as you would expect. They can be moved, resized, closed, scrolled, and exploded, within the confines of the main window. However, unlike most windows, the views can be arbitrarily zoomed by rubber-banding a rectangle while the middle mouse button is pressed.

Contained in each Hv view are Hv items. These include Hv versions of common control widgets such as sliders, buttons, rainbow scales, and wheels. There is also a suite of world-coordinate-based items, such as points, lines, rectangles, ovals, text, and polygons. More importantly, views contain user-defined items that represent real-world objects or graphs. Typically, developers only have to provide the code that draws the item (using the Hv drawing primitives). Hv will take care of the item maintenance and Postscript rendering.

Hv provides many items that already exist as Motif widgets, including buttons and other control widgets. This was done partially to provide a common interface; you do not use a Motif API for some objects and an Hv interface for others. This also supports widget sets other than Motif. The widespread acceptance of Motif and the fact it is now bundled on all UNIX workstations has allayed this concern. Still, the minimization of the use of Motif may prove beneficial in porting Hv to non-UNIX platforms such as Windows 95.

To summarize, an Hv application has a single main window that confines an arbitrary number of Hv views. Each Hv view is comprised of multiple Hv items. The items come in two basic flavors -- control widgets and user-defined representations of real objects.

Hv Views

An Hv view is comprised of three distinct areas:

  • The HotRect is the canvas where the representation of the simulation will be displayed.
  • The Control area is where application state is maintained and control items are typically displayed.
  • The Feedback region is where live updates associated with tracking the pointer are reported.

Figure 2 is a typical view arrangement which designates the three areas. Figure 2 also includes a familiar set of controls for dragging, resizing, exploding, hiding, and scrolling an Hv view.

Hv Programming

A preliminary design should be carried out prior to the development of an Hv application. This involves deciding how many different types of views are required and then deciding (for each view type) what control items are needed, what HotRect (usually user-defined application-specific) items are needed, and what manner of pointer tracking (feedback) would be informative.

The program hvmap in Figure 3 (included as a demo in the Hv distribution) displays political maps on either a Mercator or a globe-like orthographic projection. As part of a preliminary design phase, I decided to use just one view type, providing controls so the user can toggle between the two projections. Additional controls allow users to toggle the display of a latitude-longitude grid and a CPU-expensive rendering of elevation data.

The alternative was to define two distinct view types, one for each projection. I rejected this as unnecessarily complicated. One view type does not mean there can be only one view. Any Hv view can be indefinitely cloned: I can have any number of hvmap views, each a carbon copy in regard to the controls and feedback, but independently positioned, sized, and zoomed, some showing Mercator projections and others orthographic. This is a common tradeoff in an Hv application -- to choose between defining a new view type or adding controls (usually radio buttons) on a base that selects different renderings.

The main program in an Hv application generally consists of three lines: one to initialize Hv, one to call the application's private initialization, and a final call to enter an event dispatching loop. hvmap's main program is presented in Listing One

The first call handles the Hv and X/Motif initialization. Hv_VaInitialize, like many Hv routines for creating views, items, and other objects, uses NULL-terminated, variable-length argument lists, the bulk of which are keyword-value pairs initializing an attribute of the object (or, in this case, process). Variable-length routines all begin with the Hv_Va prefix. Hv initialization performs (transparently) all the underlying X and Motif initialization, creates the main window, allocates fonts and colors, and creates the menubar. Hv_Go implements an event loop where X events such as pointer motion, pointer clicks, and keystrokes are handled. You don't need to know any of the details of the Hv initialization, or how Hv processes X events.

Variable-Length Argument Lists

The use of variable-length argument lists was indispensable in making Hv programming tractable. The alternative was complicated creation signatures with many parameters or a slew of attribute-setting routines called after an item or view was instantiated. Since this technique may be of general use for any program, I will take a momentary detour to explain how variable-length argument lists were used in Hv.

A variable-length argument list in Hv looks like Hv_VaX(r1,..., rn, v1,....vn, NULL), where r1 through rn are required, v1 through vn are optional, and NULL terminates the argument list. At least one required argument must be provided; you cannot write an ANSI C procedure that has only optional arguments. (However, ANSI C doesn't require NULL termination; you could have one of the required arguments describe the optional arguments that follow. This is how printf and related procedures work in C.)

For example, the procedure for creating Hv items has one required argument, the view (of type Hv_View) where the item will live. The prototype for the function is Hv_Item Hv_VaCreateItem(Hv_View View, ...). The ellipsis is not representative -- you must actually type three periods. You will probably have to include the stdarg.h header file (or the older, non-ANSI sys/varargs.h header file) to use variable-length argument lists. The top of this procedure declares va_list ap, then calls va_start(ap, View) to initialize ap for handling the variable arguments. View is the last required variable here. The va_list and va_start symbols are part of Standard C. Once ap is initialized, you can use it as any other variable. In particular, Hv_VaCreateItem passes it to Hv_GetAttributes, which takes the va_list variable and processes it, stuffing the results into an array, which the remaining code in Hv_VaCreateItem uses to initialize the item as specified.

The only remaining issues involve how Hv_GetAttributes processes the va_list. Basically, it pulls off one argument and checks if it is the NULL terminator. If it is not, it assumes it is a keyword and pulls off the next argument as the corresponding value. It places this in a union and interprets the value based on the data type associated with the keyword. Listing Two is the code for Hv_GetAttributes.

Designing an Hv Application

When developing an Hv application, the bulk of your work goes into the private, application-specific initialization routine Init(). Here is where an application's initial views are created along with the controls, other standard and user-defined items, and entries into the feedback area. Any routine initialization (such as initializing global variables) and menubar additions or modifications are also done here. Listing Three presents the application-specific Init() procedure for hvmap.

For example, the WindowTile and AddLogo procedures use Hv primitives for tiling the main window with a propaganda graphic and drawing a corporate logo on a special welcome view present in many Hv applications. InitControls merely initializes some global variables. The InitQuickZoom procedure sets up predefined zooms for hvmap, available via a mouse-button-controlled pop-up menu. These details are best left for you to uncover when building and examining the demos at length. All of these procedures are optional.

Listing Four is the MakeMenus procedure, which modifies the default menus provided to all Hv applications. This creates, via variable-length argument calls to Hv_VaCreateMenuItem, three menu items and attaches a callback (to be invoked when the user makes that selection) to each. These particular items are attached to predefined menus -- Help and Action -- present in all Hv applications. You can also create an entirely new menu and attach items to it.

Listing Five shows the FinalInit function. Hv_AddPath adds a search path that Hv will automatically scan for data files. Hv_InitMaps initializes the maps. (The map initialization is an Hv call, Hv_InitMaps, because support for drawing maps is built into Hv, not in the hvmap demo.) The interesting line is the NewView line, which creates an initial map view. This procedure is too long to present here, but the most important part of it is the embedded call to Hv_VaCreateView, which creates the actual view; see Listing Six.

For example, the (Hv_INITIALIZE, setup) pair in Listing Six tells Hv to call a private initialization routine after the view is instantiated. In that routine (ViewSetup, in this case), the applications will attach the items (as well as the feedback instructions) to the view. Listing Seven creates the radio buttons in Figure 3 that let users toggle between projections. It actually creates a complete set of radio buttons. If a new projection is supported, you only need to add another pair (Hv_OPTION, "NewProjection") to the argument list.

Conclusion

When you create views and items, you attach callback procedures that are invoked in response to various actions. For example, you can provide a feedback procedure for a view that Hv will call when the pointer moves within the view's HotRect. You have to convert the pointer position provided by Hv and create a meaningful string for Hv to display in the appropriate position in the feedback area.

There are two ways to create user-defined items. One way is to create truly user-defined items that Hv knows nothing about. This approach requires you to provide a procedure for drawing the item as well as support procedures that tell Hv how the object should respond to scrolling, resizing, rotation, and other events. It's usually better to piggyback on an existing Hv item. For example, many items can often be represented by world-based polygons -- those with vertexes stored as world, rather than pixel, locations. Hv has a predefined world-based polygon, so creating an item based on it is advantageous because Hv already knows how to handle all the basic drawing and item maintenance. You need only provide a customized drawing procedure that Hv calls after (or in lieu of) its basic polygon drawing, effectively converting the item from generic to specific.

Since hvmap only displays maps, and maps are built into Hv, it has no need to create new items. A more serious Hv application is ced, which is used to display data in a massive (six-meter diameter) nuclear physics detector. One component of the detector is a device called a "drift chamber." Two drift chamber items are visible in Figure 1. They are in the topmost view, sitting on a multicolored background and are tiled with hexagons, some of which are filled. The code that creates one of these drift chamber items is provided in Listing Eight.

This is an example of the piggyback technique. The item is created not as a user item (with Hv_TYPE of Hv_USERITEM), but as a world polygon, from the predefined suite of Hv items. After instantiation, the type is changed to a user item, and the drawing is redirected to a user-provided drawing routine.

An alternative is to use the Hv_USERDRAW attribute to specify a drawing routine. With this approach, Hv will first call its built-in world polygon drawer, then call the user provided routine. By overriding the standarddraw pointer (as in Listing Eight), the default world polygon drawer is never called.

DDJ

Listing One

Hv_VaInitialize(argc,         argv,         Hv_NORAINBOW,         True, 
        Hv_USEWELCOMEVIEW,    True, 
        Hv_VERSIONNUMBER,     101, 
        Hv_USEEXTENDEDCOLORS, True, 
        NULL); 
Init(); 
Hv_Go(); 

Back to Article

Listing Two

void Hv_GetAttributes(va_list ap,              Hv_AttributeArray attributes,
              char **optlist,
              int *tags,
              int *fonts,
              int *colors,
              short *numopt)
{
  int              argflag = -1;
  int              prevargflag;
  /*give defaults for all attributes */
  Hv_SetDefaultAttributes(attributes);
/* now get the attributes */
  while (argflag != 0) {
    prevargflag = argflag;
/* the argflag is the keyword, #defined as a set of sequential ints such as 
#define Hv_BACKGROUND 22, etc. the NULL terminator will produce a zero 
argflag */
    argflag = va_arg(ap, int);  /* should be >= zero & < some max */
    if ((argflag < 0) || (argflag >= Hv_NUMATTRIBUTES)) {
    /* unintersting error handling omitted */
    }
    else if (argflag > 0) {
/* group according to type, place in the attribute array which is an array of 
unions. We need insider information about what type is associated with each 
attribute. */
      if ((argflag == Hv_DRAWCONTROL)
      || (argflag == Hv_USERDRAWCONTROL)
       /* or many integer attributes omitted */
      || (argflag == Hv_RELATIVEPLACEMENT))
         attributes[argflag].i = va_arg(ap, int);
      else if ((argflag == Hv_STATE)
      || (argflag == Hv_USEWELCOMEVIEW)
       /* or many short attributes omitted */
      || (argflag == Hv_PLACEMENTGAP))
          attributes[argflag].s = (short)va_arg(ap, int);
/* omitting code for other types: chars, strings, floats, etc. */
      else  /* only thing left are the pointers */
    attributes[argflag].v = va_arg(ap, void *);
    } /* end argflag > 0 */
  }
  va_end(ap); /* terminate the processing */
}

Back to Article

Listing Three

void Init() {   Hv_canvasColor = Hv_lightSeaGreen; /*Change BG color */ 
   WindowTile();    /* tile the window with propaganda */ 
   MakeMenus();     /* modify the main menu */ 
   InitControls();  /* Initialize controls */ 
   AddLogo();       /* add a logo */ 
   InitQuickZoom(); /* initialize quick zooms */ 
   FinalInit();    /* final initialization */ 
}

Back to Article

Listing Four

     void MakeMenus() {    Hv_Widget item; 
    char *text; 
/* create the menu items for the Help menu */
    text = (char *)Hv_Malloc(strlen(Hv_programName) + 20);
    strcpy(text, "About "); 
    strcat(text, Hv_programName); 
    strcat(text, "..."); 
    item = Hv_VaCreateMenuItem(Hv_helpMenu, Hv_LABEL, text, 
                  Hv_CALLBACK, DoAboutDlog, NULL) 
   Hv_Free(text);
/* add a "new view" item to the action menu */ 
    item = Hv_VaCreateMenuItem(Hv_actionMenu, 
                   Hv_LABEL,     "New View", 
                   Hv_CALLBACK,  GetNewView, 
                   NULL);
/* add a plot canvas option to the action menu */ 
    item = Hv_VaCreateMenuItem(Hv_actionMenu, Hv_LABEL, "New Plot Canvas", 
                   Hv_CALLBACK, NewPlotView, NULL); 
     }

Back to Article

Listing Five

static void FinalInit() {  Hv_View view; 
  Hv_AddPath("./mapmaker");   /* add to search path */ 
  Hv_InitMaps("hv.maps");     /* initialize maps */ 
  view = NewView(MAPVIEW); }  /* create one initial view*/ 
}

Back to Article

Listing Six

Hv_VaCreateView(&View,     Hv_TAG, tag, 
    Hv_DRAWCONTROL,
    Hv_STANDARDDRAWCONTROL+Hv_SAVEBACKGROUND,
    Hv_POPUPCONTROL, popupcontrol, 
    Hv_TITLE, tchr, 
    Hv_INITIALIZE, ViewSetup, 
    Hv_CUSTOMIZE, ViewCustomize, 
    Hv_USERDRAW, ViewDraw, 
    Hv_OFFSCREENUSERDRAW, OffScreenViewDraw, 
    Hv_FEEDBACK, ViewFeedback, 
    Hv_LEFT, left, 
    Hv_TOP, top, 
    Hv_XMIN, xmin, 
    Hv_XMAX, xmax, 
    Hv_YMIN, ymin, 
    Hv_YMAX, ymax, 
    Hv_HOTRECTWIDTH, width, 
    Hv_HOTRECTHEIGHT, height, 
    Hv_MINZOOMWIDTH, minw, 
    Hv_MAXZOOMWIDTH, maxw, 
    Hv_MINZOOMHEIGHT, minh, 
    Hv_MAXZOOMHEIGHT, maxh, 
    Hv_SIMPROC, (Hv_FunctionPtr)SimulationCB, 
    Hv_SIMINTERVAL, 2000, 
    Hv_HOTRECTVMARGIN, 7, 
    Hv_HOTRECTCOLOR, Hv_blue-2, 
    Hv_MOTIONLESSFB, True, 
    Hv_MOTIONLESSFBINTERVAL, 500, 
    NULL); 

Back to Article

Listing Seven

Choice = Hv_VaCreateItem(View,     Hv_TYPE, Hv_CHOICESETITEM, 
    Hv_RELATIVEPLACEMENT, Hv_POSITIONBELOW, 
    Hv_PLACEMENTITEM, Box2, 
    Hv_PLACEMENTGAP, 1, 
    Hv_NOWON, mdata->projection, 
    Hv_FILLCOLOR, Hv_powderBlue, 
    Hv_ARMCOLOR, Hv_navyBlue, 
    Hv_COLOR, Hv_gray10, 
    Hv_FONT, Hv_fixed2, 
    Hv_OPTION, "Mercator", 
    Hv_OPTION, "Orthographic", 
    Hv_SINGLECLICK, ProjectionChoiceCB, 
    Hv_TITLE, "Map Projection", 
    NULL); 

Back to Article

Listing Eight

Item = Hv_VaCreateItem(View,     Hv_TYPE, Hv_WORLDPOLYGONITEM, 
    Hv_TAG, DCITEM, 
    Hv_NUMPOINTS, np, 
    Hv_DATA, fpts, 
    Hv_NUMROWS, 1, 
    Hv_NUMCOLUMNS, 1, 
    Hv_DOMAIN, Hv_INSIDEHOTRECT, 
    Hv_DOUBLECLICK, EditDCItem, 
    Hv_DRAWCONTROL, Hv_ZOOMABLE + Hv_INBACKGROUND,
    Hv_AFTEROFFSET, DCAfterOffset, 
    Hv_USER1, (int)superlayer, 
    Hv_USER2, (int)sect, 
    Hv_BALLOON, (void *)text, 
    Hv_FIXREGION, FixDCSuperLayerRegion, 
    NULL);
Item->type = Hv_USERITEM;        /* redefine as user item */ 
Item->standarddraw = DrawDCItem; /* redirect std. drawing */ 

Back to Article


Copyright © 1998, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.