Skip to content

WAGS: a Graphics Specification Language for ASL

Vector 7:3, page 41

graphics mathematics and statistics

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 41

Introduction

ASL, the APL Statistics Library, has been reported on several times in ‘Vector’. The objective is to establish a comprehensive library of APL functions for use by statisticians. APL is a particularly suitable language in which to explore possible analyses of data, but the spread of APL in the statistics community has been hindered by the need for the user to program all the common statistical operations from scratch. ASL will attempt to remove this blockage, making statistics in APL as accessible as the huge wealth of statistical packages currently available. ASL will improve on such packages in two ways. First, it will not be a closed system, but rather the whole power of APL will be available to the statistician. Second, ASL has the support of several eminent academic statisticians. Their input will help to ensure that ASL functions are accurate, reliable and represent the best in current statistical methodology.

Graphical presentations are an important part of modern statistical analysis. Clearly, ASL must meet this need by providing functions that produce graphical output where appropriate. The major problem is the great variety of graphical output devices and the absence of a standard way for APL interpreters to handle graphics. We would like the ASL functions to be as portable as possible. We certainly do not want different versions of every function using graphics, for each different graphics system that might be available to the user.

Warwick University faced the same problem with its ‘APL in Statistics’ project. That project was supposed to produce software for teaching Statistics using APL which was to be freely available to the academic community. It was not desirable to make the software dependent on a single graphics system, nor to rewrite everything for a range of systems. The answer was to define a core set of routines to perform all primitive graphical functions. This core would need rewriting for each new system, but all the software would then be immediately portable to any system on which the core had been implemented. The resulting graphics specification language was called the Warwick APL Graphics Standard, or WAGS. The first version of WAGS was created by Ewart Shaw, and subsequently many important extensions were added by Andy Pole.

A thoroughly revised version has been adopted as the definitive specification of the ASL graphics primitive language. In preparing this revision the author received enormous help from Alan Sykes (University College of Wales, Swansea), Ewart Shaw (University of Warwick), Andy Pole (Bull Secure Systems) and John Hinde (University of Exeter). All of these people are co-authors of WAGS.

This article describes the main design features of WAGS. Another document - ‘WAGS: Function Specifications, Version 1’ - presents a full specification of WAGS, and is available from the author. The full specification will be of interest to three different groups of people. First, WAGS must be implemented on a variety of APL systems, so the specification serves as a blueprint for the WAGS implementer. Second, ASL will contain many functions which create graphics at a much higher level than WAGS, but they will all do so by calls to the WAGS primitives: the specification of WAGS acts as a user manual for ASL graphics programmers. Finally, the end user of ASL may have occasional need to understand the WAGS system that underlies the ASL graphics functions. After all, the initial audience for ASL will comprise statisticians who already know and use APL. ASL is deliberately an open collection of functions, not a package. The knowledgeable user is free to modify functions in ASL for his or her own purposes, and will need to understand WAGS in order to modify graphics functions.

The following sections describe the key features of WAGS.

Getting in and out

The basic usage of WAGS for any graph is

GOPEN

code to draw graph

GINTER
GCLOSE

GOPEN initialises graphics. This is physically necessary in an IBM-PC environment to switch from the text screen to the graphics screen. In many other systems there will be a need to do some initialising of the hardware, and there will always be some software housekeeping to do. GCLOSE simply closes down the graph. It may switch back to the text screen on a PC, return the pen to rest on a plotter, etc.

GINTER is a very important preliminary to GCLOSE, and it may also be invoked at other stages in drawing the graph. At the very least, it will halt operation until the user presses a key. This is necessary on a PC to stop the graphics screen disappearing before the user has had a chance to look at it, but the pause effect can be useful elsewhere. More importantly, GINTER is intended to allow the user to add his/her own things to the graph, interactively. He/she may wish to add a caption, draw an arrow to highlight one point in the graph, delete something, etc. The range of such features provided by GINTER is not specified in WAGS. There are several reasons. First, it is no concern of the programmer, since GINTER simply gives the user control where the programmer leaves off. Second, the available GINTER options will be system dependent. For instance, a screen might allow deletions but a pen plotter will not. Third, specifying GINTER would deny implementers of WAGS the freedom to provide the best features which each system can provide. Fourth, WAGS can be implemented quickly on a system by providing a skeleton GINTER, and this can then be enhanced at leisure.

Ambiguity in WAGS

There are other places in WAGS where the precise functionality is not specified but is left to the implementer to define. How does this ambiguity affect the three groups of people who might use the specification document? First, it gives the implementer freedom to exploit the system’s graphical capabilities to the full.

For the programmer of higher level ASL graphics functions the ambiguities in WAGS are a great simplification. For instance, to distinguish between plotted lines that represent different things, he/she might wish to plot them in different colours or different types of line (e.g. dotted or dashed). WAGS does not require the programmer to know (or to enquire) whether the graphics device on which the program will run has a colour capability, or what kinds of dotted and dashed lines it can draw. The programmer simply asks for colour-type combination i. What this produces is left to the implementer, and it will certainly produce a different effect on different implementations. The ambiguity is intentional. To provide facilities for the programmer to enquire about all possible configurations, and then to require the programmer to handle all these possibilities would make WAGS enormously complex, and would make the programmer’s task very difficult. Instead, the programmer only needs to know that colour-linetype i is different from colour-linetype j.

For the user of ASL, the ambiguity is no problem either. No amount of clever programming could be guaranteed to produce graphs that always satisfy the user completely. But if the implementer has been able to set up pleasing colour-line-type combinations, etc., then ASL graph drawing functions should produce good graphs with minimal effort from the user. And if the user would prefer different colours, shadings, etc., WAGS provides a very simple way of making those changes. A function GCONFIG allows him/her to over-ride the default sets of colours, line types, colour-line-type combinations, etc., provided by the implementer. Then running the graph again with the new settings will achieve the desired effect.

Windows and Scaling

A favourite graphical technique is to define different areas of the screen (or page) as windows, each addressed with its own coordinates. WAGS covers this idea very simply by allowing the whole screen to be addressed in variable coordinates. By default, GOPEN initially defines the screen so that the lower left-hand corner is (0,0) and the upper right (1,1). We say the screen has coordinates (0,0;1,1). If we then want to refer to the upper left-hand quarter of the screen as a window, with its own coordinates (0,0;1,1), this is equivalent to giving the whole screen coordinates (0,-1;2,1). WAGS allows new screen coordinates to be set up at any time by assigning new coordinates to any pair of points. So instead of defining the above example as rescaling (0,0;1,1) to (0,-1;2,1), we could have said it was rescaling (0, 1/2;1/2,1) to (0,0;1,1). This is done by a function GSCALE.

This general approach is very convenient in drawing conventional (x,y) graphs. The screen scaling is simply defined to agree with the natural (x,y) scales of values. So if the x values range from x1 to x2 and the y values from y1 to y2, and if we want to plot these values in a part of the screen whose bottom-left and top-right corners are (a,b) and (c,d) in the current scaling, the new scaling is created by:

(A,B,C,D) GSCALE (X1,Y1,X2,Y2)

Function GASPECT returns the aspect ratio of the screen/page, so whereas GOPEN initially establishes the screen coordinates as (0,0;1,1), the statement

0 0 1 1 GSCALE 0 0 1,GASPECT

will then change the scaling so that equal scale distances in the x and y directions correspond to equal physical distances on the screen. Whereas GASPECT always gives the true aspect ratio, an aspect ratio relative to any current scaling can be found using a function GANGLE, which reports the true angle formed by any two points given in the current scaling.

Colour

Where colour is available, different systems have different ways of referring to colours; some have only a few available and some have a vast range. In WAGS, GOPEN establishes a list of available colours and the programmer simply requests colour i in that list. The programmer will not know what colour ‘colour i’ is, simply that it is different from the other colours. Function GCOUNT reports how many different colours are available. On a monochrome system this will be one. On a colour system, GOPEN will set a default number and sequence of colours, but the user can over-ride these with GCONFIG. He/she can therefore change the sequence of colours, or even make a colour system behave as monochrome.

Types of Line

Some systems can draw lines as dots or dashes, or in different thicknesses. WAGS needs to make full use of whatever features are available. The technique is the same as for colour. There is a list of different types of line available, created by GOPEN, which may be the default sequence or specified by the user with GCONFIG. As a general rule, programmers wishing to distinguish between two lines in a graph can use colour or line type or both. The program might use colours if enough are available, and otherwise use line types. WAGS provides another feature to simplify this process. When drawing lines, the programmer can either specify both colour and line type separately, or give a single number to select a colour-line-type combination.

Shading

Another variable feature of graphics devices is the ability to fill an area of the page or screen with colour. Even if that is not possible, as on a pen plotter, a comparable visual effect is provided by cross-hatching the area. GOPEN establishes a default sequence of types of shading. Different WAGS implementations may interpret shading in terms of various forms of hatching, various shades of colour (e.g. various grey shades on a monochrome screen) or various dot-densities. Again, the user can over-ride the default sequence by GCONFIG.

Text

Some systems can write text in various fonts in a range of sizes. Now different colours, line types or shadings are generally used simply to distinguish between similar graphical elements. The situation is different with text. Labelling of graphs should mostly be done consistently in one font and size. Changes of font are not needed just to distinguish between things; the distinctions are contained in the text itself. Using many different fonts or sizes tends to create a very messy picture. Nevertheless the user may want some font changes. The caption to a graph may be preferred in a bold or italic font, or in a larger size, for instance.

The implications of these comments are as follows. First, WAGS recognises a sequence of fonts, in the same way as there are sequences of colours, line types and shadings. (The same font style in a different size is technically understood as being a different font, so the notion of a font encompasses both style and size.) However, GOPEN establishes the default sequence as containing only one font. The programmer should nevertheless call for different fonts wherever the user might conceivably want different fonts. Now the convention in WAGS is that the sequences of colours, line types, shadings and fonts wrap around, e.g. asking for colour 6 when only 5 are available yields colour 1, asking for colour 7 yields colour 2, and so on. Therefore, with only one font available this font will be provided whichever font number is requested. This devious technique allows the user to obtain whatever fonts he/she requires simply by over-riding the GOPEN default sequence.

Another feature of fonts is that the user or programmer will often need to know how much space a piece of text will occupy in a given font. A function GSPACE is provided for this purpose.

Global Variables

A cleaner system results if the programmer is working always through functions, rather than some of the operations being to assign values to pre-defined global variables. For instance, to redefine the current scaling of the screen/page it is neater to use the function GSCALE as above rather than assigning a value to a global variable. When some operations are functions and some are not, it is hard to remember which is which. Also, it is easier to work with a hard-copy printout of a workspace when the only things of interest are functions.

Global variables will of course be needed within WAGS (to store the current scaling, for instance), but the implementer is free to define them in the most natural and efficient way. Neither the user nor the programmer need know what they are or what they do.

Redrawing

Graphs frequently display the results of quite extensive calculations. When we wish to redraw a graph it would be tedious to repeat those calculations. If the calculations and graphics are in separate functions, then the graph can be redrawn quickly, but only if a copy of the workspace has been saved with all the necessary variables. ASL provides the following facility for redrawing graphs.

A function GSAVE is called to switch on the graph saving feature. Then as each core WAGS function is invoked, a transcript is built up of the graphical functions being used. The transcript is actually a function which, when called, redraws the graph. (Another function GFIX finally fixes this function from a canonical representation which is built up by the other functions.) In the transcript, all arguments will be explicit numeric data, so it is not necessary to refer to any other variables: saving the single transcript function saves the graph. Redrawing the graph is quick because the explicit arguments incorporate the results of all necessary calculations. Interactive drawing commands issued from the keyboard to GINTER are recorded in the transcript, too. The transcript may be edited before being redrawn, for instance to include changes of line type.

GSAVE and GFIX need not be used to save an entire graph. For instance, calling GSAVE before and GFIX after a complicated sequence of operations to draw a set of axes will yield a function that can quickly redraw those same axes as part of another graph. The graph saving feature is another reason for requiring all operations to be done through functions, because it is not possible to trap and record assignments to global variables.

Graph saving could have been provided as an entirely separate implementation of WAGS. In that case, after the graph saving WAGS implementation had been copied into the workspace, a graphics function in ASL would not actually draw on any device, but would produce the saved transcript instead. However, it is more convenient in practice to have the transcript being saved simultaneously with the graph being produced on the screen. If interactive drawing operations performed with GINTER are also to be saved, then it becomes essential to combine saving with drawing in this way. A user is, of course, likely to have two or more different versions of WAGS. The WAGS implementation that draws on the screen, for whatever APL interpreter and hardware he/she is using, will be his/her main version of WAGS, and will include graph saving. Another implementation might drive a laser-printer by producing a PostScript file. After previewing the graph on the screen, saving the final version, and perhaps editing the saved function, the user will simply run the saved function through this other WAGS implementation to produce the hard copy.

Running the saved function through another implementation of WAGS will not necessarily produce an identical graph. This is to be expected because different devices will have different capabilities. It is, however, important to realise that running the saved function on a different device may even produce a different result from running the original function on that device. If the original function calls GSPACE to ensure that a piece of text will fit a given space, then the saved function will place it there. On the new device the same text in the same font number may not fit the space, and running the saved version of the function will produce an unsightly result. Running the original function would call GSPACE afresh, find that the text will not fit, and could take appropriate alternative action. Saving a graph is a reliable way of rerunning it through the same implementation of WAGS. When using a different WAGS implementation, however, such as for producing a printout, it will often be necessary to run the original function again to achieve the best result.

Second Generation APL

ASL supports second generation APLs to the extent of allowing nested arrays as arguments to ASL functions where appropriate. Therefore in the detailed WAGS specifications there are frequently alternative forms of arguments. A user with a second generation interpreter may use nested arrays, but may also use the same simple array arguments as a user with first generation APL.

Higher Level Graphics

WAGS simply defines the lowest level of graphics operations in ASL. As mentioned before, along with the statistical functions which form the major part of ASL will be other functions to create appropriate graphical displays. These will operate entirely through calls to the WAGS functions, so that such functions will be portable to any system for which a WAGS implementation is available. To make the job of creating those functions easier, ASL will also offer a range of higher level graphics building blocks. For instance, there will be various functions useful for setting up and drawing axes. Axis drawing is a task that almost all higher graphics functions in ASL will need to do, and it is a complex and difficult task to do well. The higher graphics facilities will make axis drawing a simple and routine operation. Other building blocks will be available to draw standard graphical elements like histograms and box and whisker plots. However, those functions are quite separate from the low level WAGS system which is the subject of the present article.

Their design and specification is another story.