Perry is a senior systems analyst for Arco Alaska Inc. and can be contacted at laspws@aai.arco.com.
To a large extent, application development and GUI development have become one and the same. The result is the advent of GUI builders that simplify the process of building bit-mapped interfaces for systems such as Windows and Motif. But even though GUI builders provide a framework for putting an application together, you still need to deal with the details, particularly those of the user interface--and that's where UI widgets come into play.
Widgets are prebuilt display objects and user-interface controls that can be pulled intact into applications, saving months of tedious coding work. In our case, for instance, widgets for x/y graphing and 3-D visualization made it possible to construct a complex application in a very short time period. The application, Unimovie, is a C++ application for postprocessing reservoir-simulation results; see Figure 1. The application presents 2-D and 3-D views of oil-field values such as oil saturation, water saturation, and pressure. Time-lapsed animations give engineers a sense of frontal advances, viscous gravity fingers, and flow patterns, as if they were watching them in real time--somewhat like a TV weather map.
In the past, Arco engineers used a custom-built Macintosh application to perform the same basic tasks as Unimovie, but that program's execution time for large simulations was unacceptable. Also, parts of the Mac application were written in assembly language, which made maintenance difficult.
Advancements in RISC technology provided us with the incentive to write a similar application that would run under UNIX. However, the time frame was tight--we needed to complete Unimovie in six months or not bother at all. We quickly realized that the key to completing the project on schedule was to minimize C++ coding by using high-level tools for subclassing and GUI development. Since I was familiar with the Tools.h++ environment from Rogue Wave and the KL Group's off-the-shelf widget components for graphing, plotting, and 3-D displays, these toolsets became the foundation upon which all our application classes were written.
Unimovie, which was originally written for an RS/6000 running AIX, consists of about 14,000 lines of C++ code and compiles into an approximately 2-MB executable. We've subsequently ported Unimovie to Hewlett-Packard and Sun workstations. On all three platforms, we used a vendor-provided C++ compiler.
One type of Unimovie display is similar to a contour map, with values such as oil saturation projected on the top of the contoured picture. In this instance, however, the colors represent a value, not a depth. In other words, it is really a 4-D plot: Three dimensions are spatial, and the fourth is the value projected on the surface. Other types of Unimovie displays are custom-developed, 2-D projections of these 3-D surfaces.
The illusion of animation is achieved with carefully synchronized time-out calls within X Windows (XtAppAddTimeOut), which allow Unimovie to display the same time steps simultaneously as several planes are coordinated and displayed at once. Time-out calls let the program delay for short intervals, then draw the next image in the time sequence. An interval ID is passed which tells the program how long to wait, and the careful synchronization of these intervals puts the entire display in motion. In this way, the user can see an x-y plane and cross section at the same time, for example. Unimovie can display an animated sequence of time steps for any plane--depth or cross section--in a reservoir-simulation study.
Imagine a rectangular grid comprised of cells--like a spreadsheet, but with a much greater resolution. Each cell within the grid can independently display a spectrum of n different colors. These colors represent n uniform intervals in the data range (from MIN to MAX), with each successive interval assigned the next color in the spectrum.
As the reservoir-simulation data is read, the values in the range are displayed with their corresponding colors. The colors change with time to represent the dynamic properties of the simulation data. These changing color patterns can be grasped much more intuitively than mere numbers or still graphs.
Unimovie's Motif encapsulation is similar to that outlined by Douglas Young in Object-Oriented Programming with C++ and OSF/Motif (Prentice-Hall, 1992). In general, we created a parent class to handle just the GUI components. The parent class was an abstract base class with several no-op virtual functions. Children of this parent class were generated to fill in the details of the virtual functionality. This approach may allow us to port to PowerPC or Intel machines while replacing only the parent/GUI classes.
User-interface development can be complex and time consuming, and many developers spend much of their time wrestling with low-level graphics primitives and X Window idiosyncrasies. Well-designed widget sets such as XRT allow X developers to concentrate on the content of their applications and not the details of Xlib.
XRT widgets are object-oriented extensions to Motif. KL Group's XRT/3d for Motif is the basis for Unimovie's 3-D surface plots and contour graphing. With Unimovie, users display not only a 2-D view of an x-y plane or cross-section, but a 3-D, depth-projected picture of a surface. The widget handles rotation, scaling, annotation, and perspective calculations using built-in X translations.
We used the XRT/graph widget to display the simulation data in various 2-D graphs and bar charts. The 2-D graphs were used for simulation-history matching with historical data retrieved from an Oracle database. XRT/graph handles all of the typical business charting types, including x-y plots, pie charts, and area graphs. The widget isolates the developer from tedious tasks such as axis scaling and precision. We have used XRT/graph for high-performance, real-time displays without any noticeable degradation in speed.
XRT includes many convenience functions to simplify development. For example, XrtMap handles all user-to-pixel coordinate conversions. Example 1 shows how XrtMap is used to mark a selected point with crosshairs. Hardcopy output is also encapsulated with convenience calls. Output can be produced in EPS, XWD, or CGM formats. For our custom 2-D C++ classes, we developed our own PostScript methods. C++ function overloading permits us to use the same method name for both screen painting and 2-D PostScript output. The function-argument signature determines whether data is drawn on the screen or to a PostScript-output stream.
The XRT widgets are easier to learn than any other widget set I've used for X Window development. XRT widgets are programmed using the same Xt-based application-programming interface as OSF/Motif. In addition, XRT provides a tool called "Builder" that allows the developer to interactively witness the effects of various resource changes upon graph appearance. This is a valuable tool during the application-prototyping phase or for producing presentation-quality plots.
Additional procedures and methods handle special tasks, such as printing. The toolkit includes sample code for creating print dialog boxes, making it relatively easy to provide high-quality printing output in EPS, XWD, or CGM formats. Note that PostScript methods have been built into the graphic 2-D classes. This is another example of virtual functions/late binding. Function overloading allows us to use the same method name for both screen painting and PostScript printing. This greatly simplifies the main event loop, while the gnarly details are hidden far below. The arguments of the method, in this case, determine whether graphics are drawn on the screen or a PostScript file is generated.
Compared to other GUI development tools I've used, the XRT tools make for a quick learning curve because the XRT widgets are programmed using the same Xt-based application-programming interface as OSF/Motif.
The other primary tool we used was Rogue Wave's Tools.h++, a data-structure toolkit that shields the developer from a lot of nitty-gritty C++ coding. Tools.h++, which is available for DOS, Windows, OS/2, and UNIX, encapsulates common C++ constructs such as strings, dates, linked lists, binary trees, stacks, queues, and file I/O. All these constructs are presented in template form.
Unimovie is based entirely upon templates. Tools.h++ requires only a few class-member functions to be present in order to use their templates. With Tools.h++, issues such as dynamic memory allocation and vector resizing can be eliminated from the system-development equation. This is a significant step toward code simplicity and bug reduction. Tools.h++ also provides classes that mimic the Smalltalk paradigm.
The late binding of C++ was an important feature to us because Unimovie uses different C++ classes to display different kinds of grids: rectangular, corner point, and polar coordinate. The type of simulator data determines which grid type is created. At run time, the same method names are used for all three grid types, but the correct function signature is chosen using late binding. Example 2 illustrates how Tools.h++'s templates and late-binding properties allowed us to put all types of overlays (text, boxes, lines, and the like) into one type of container. This example demonstrates the concept of resizing all graphic overlays when the window container is resized.
Another example of late binding is method drawGrid, a virtual function defined for all three grid types. At run time, the correct function will be picked. Tools.h++ templates organize collections of these grids, with the virtual base class as the parameterized type. Actual members of the collection are, of course, children of the base class that have a definite grid type.
One of the problems we encountered was obtaining adequate database-access times with a relational data structure. Some of the simulations take up as much as half a gigabyte of information. Unimovie parses through these giant files to determine distinct file pointers to indicate the relevant time steps. Based on these pointers, it builds a direct-access dictionary to read the file.
This means there is an initial delay while Unimovie reads the file for the first time. From then on, whenever you want to pull a particular time sequence, you don't have to parse it from scratch. The information dictionary of "hot spots" is stored in memory, which allows for very fast access times for all subsequent movie runs.
We initially used Oracle's Oracle7 to store data, but we soon realized our unique set of problems didn't lend themselves to the typical relational-database approach. Consequently, we created our own object database using RWBTreeOnDisk, Tools.h++'s binary disk tree. This is a persistent B-Tree dictionary, in which both a dictionary and associated objects can be stored on a disk. For example, if users are interested in several oil-production quantities and want to graph just those quantities within the time series, they can extract those quantities and label them for reference in this object-oriented database. We found this custom database to be both powerful and extensible. Additionally, retrieval is about two times faster than similarly organized data in an Oracle7 database.
Standard UNIX utilities such as Lex and Yacc also simplified our development efforts. We used these utilities to create time-valued overlay images, which are analyzed, stored in ASCII files, and added to a Unimovie display. This allows users to overlay lines, images, and 2-D pictures on top of the oil-simulation images. For example, a user might want to depict a building (an oil-production facility, for instance) on top of a reservoir map.
Lex breaks up the overlay ASCII files into keywords and parameters. Yacc analyzes the sequence and patterns of keywords and parameters and processes them into graphic primitives that can be placed appropriately within the C++ grids. (More recently, we've begun using Bison instead of a vendor-specific Yacc because of Bison's consistency across multiple UNIX platforms and its support of reentrant grammars. We're about to turn to Flex instead of a vendor-specific Lex for similar reasons.)
Lex and Yacc are also used by Unimovie to read and process a general x/y plot in an ASCII format. This allows us to reformat data from various sources (including relational database and flat files) and compare it with the simulation results.
In a related way, Lex and Yacc are used as a curve-formula processor. The formula processor accepts normal syntax for curve addition, scalar multiplication, vector multiplication, division, and so forth. Formulas are stored in object-oriented databases (RWBTreeOnDisk) as text strings and retrieved and parsed by Lex/Yacc as needed. Formula recursion (formulas within formulas) is currently handled by providing multiple lexers with different global name spaces, although the reentrant grammar of Bison may make this approach obsolete.
After initial testing in Anchorage, word of our application spread throughout Arco. Unimovie has since been installed at Arco offices around the world. Engineers love the 3-D effects, screen presentations, quality of hardcopy output, and the ability to display their simulation data more quickly than in the past.
There are no run-time fees or royalties associated with the packaged widgets or any other standard components used, which has made it economically feasible to widely distribute the application.
There are many potential uses for this type of animation application. For example, environmental-studies applications could be constructed that show sequences, as in a groundwater-contamination study.
Figure 1: Sample Unimovie display.
KL Group
260 King Street East, Third Floor
Toronto, ON
Canada M5A 1K3
800-663-4723
Rogue Wave Software
P.O. Box 2328
Corvallis, OR 97339
800-487-3217
Example 1: Use of the XrtMap call from XRT/graph. XrtMap allows users to highlight the selected point with crosshairs.
Example 2: Code simplification resulting from Tools.h++ templates and the late-binding properties that allow us to put all types of overlays (text, boxes, lines, and so on) into one type of container. This sample demonstrates the resizing of all graphic overlays when the window container is resized.
Copyright © 1995, Dr. Dobb's Journal
...
// Draws a crosshair about the selected point.
void xrtWindow::position_cross ( XButtonEvent* event )
{
XrtMapResult m;
if ( XrtMap ( graph, 1, event->x, event->y, &m ) == XRT_RGN_IN_GRAPH) {
XtVaSetValues ( graph,
XtNxrtXMarkerShow, TRUE,
XtNxrtYMarkerShow, TRUE,
XtNxrtXMarker, XrtFloatToArgVal(m.x),
XtNxrtYMarker, XrtFloatToArgVal(m.y),
NULL, NULL );
}
else {
XtVaSetValues ( graph,
XtNxrtXMarkerShow, FALSE,
XtNxrtYMarkerShow, FALSE,
NULL, NULL );
}
}
...
...
// Scale all the overlays.
// Hash table iterator for RWTPtrHashTable template.
RWTPtrHashTableIterator<anyOverlay> it(overlays);
// 'anyOverlay' virtual base class for all overlays.
anyOverlay* coll;
// Simply loop through all overlays and resize them!
// All overlays have method 'handleResize' and use
// late binding to determine which method signature to use.
while ( coll = it() )
coll->handleResize (scaleHor, scaleVer, _minX, _minY, wellSize);
...