### 21 Producing Graphical Output (Plots)

21.1 The Plot Model
21.2 Plotting Symbols
21.3 Plotting Geodesic Curves
21.4 Plotting Curves Parallel to Axes
21.5 Plotting Generalized Curves
21.6 Clipping
21.7 Using a Plot as a Mapping
21.8 Using a Plot as a Frame
21.9 Regions of Valid Physical Coordinates
21.10 Plotting Borders
21.11 Plotting Text
21.12 Plotting a Grid
21.13 Controlling the Appearance of Sub-strings
21.14 Producing Logarithmic Axes
21.15 Choosing a Graphics Package

Graphical output from AST is performed though an Object called a Plot, which is a specialised form of FrameSet. A Plot does not represent the graphical content itself, but is a route through which plotting operations, such as drawing lines and curves, are conveyed on to a plotting surface to appear as visible graphics.

#### 21.1 The Plot Model

When a Plot is created, it is initialised by providing a FrameSet whose base Frame (as specified by its Base attribute) is mapped linearly or logarithmically (as specified by the LogPlot attribues) on to a plotting area. This is a rectangular region in the graphical coordinate space of the underlying graphics system and becomes the new base Frame of the Plot. In effect, the Plot becomes attached to the plotting surface, in rather the same way that a basic FrameSet might be attached to (say) an image.

The current Frame of the Plot (derived from the current Frame of the FrameSet supplied) is used to represent a physical coordinate system. This is the system in which plotting operations are performed by your program. Every plotting operation is then transformed through the Mapping which inter-relates the Plot’s current and base Frames in order to appear on the plotting surface.

An example may help here. Suppose we start with a FrameSet whose base Frame describes the pixel coordinates of an image and whose current Frame describes a celestial (equatorial) coordinate system. Let us assume that these two Frames are inter-related by a Mapping within the FrameSet which represents a particular sky projection.

When a Plot is created from this FrameSet, we specify how the pixel coordinates (the base Frame) maps on to the plotting surface. This simply corresponds to telling the Plot where we have previously plotted the image data. If we now use the Plot to plot a line with latitude zero in our physical coordinate system, as given by the current Frame, this line would appear as a curve (the equator) on the plotting surface, correctly registered with the image.

There are a number of plotting functions provided, which all work in a similar way. Plotting operations are transformed through the Mapping which the Plot represents before they appear on the plotting surface.37 It is possible to draw symbols, lines, axes, entire grids and more in this way.

#### 21.2 Plotting Symbols

The simplest form of plotting is to draw symbols (termed markers) at a set of points. This is performed by astMark, which is supplied with a set of physical coordinates at which to place the markers:

#include "star/ast.h"
#define NCOORD 2
#define NMARK 10
double in[ NCOORD ][ NMARK ];
int type;
...
astMark( plot, NMARK, NCOORD, NMARK, in, type );

Here, NMARK specifies how many markers to plot and NCOORD specifies how many coordinates are being supplied for each point.38 The array “in” supplies the coordinates and the integer “type” specifies which type of marker to plot.

#### 21.3 Plotting Geodesic Curves

There is no Plot routine to draw a straight line, because any straight line in physical coordinates can potentially turn into a curve in graphical coordinates. We therefore start by considering how to draw geodesic curves. These are curves which trace the path of shortest distance between two points in physical coordinates and are the basic drawing element in a Plot.

In many instances, the geodesic will, in fact, be a straight line, but this depends on the Plot’s current Frame. If this represents a celestial coordinate system, for instance, it will be a great circle (corresponding with the behaviour of the astDistance function which defines the metric of the physical coordinate space). The geodesic will, of course, be transformed into graphics coordinates before being plotted. A geodesic curve is plotted using astCurve as follows:

double start[ NCOORD ], finish[ NCOORD ];
...
astCurve( plot, start, finish );

Here, “start” and “finish” are arrays containing the starting and finishing coordinates of the curve. The astOffset and astDistance functions can often be useful for computing these (§7.11).

If you need to draw a series of curves end-to-end (when drawing a contour line, for example), then a more efficient alternative is to use astPolyCurve. This has the same effect as a sequence of invocations of astCurve, but allows you to supply a whole set of points at one time. astPolyCurve then joins them, in sequence, using geodesic curves:

#define NPOINT 100
double coords[ NCOORD ][ NPOINT ];
...
astPolyCurve( plot, NPOINT, NCOORD, NPOINT, coords );

Here, NPOINT specifies how many points are to be joined and NCOORD specifies how many coordinates are being supplied for each point. The array “coords” supplies the coordinates of the points in the Plot’s physical coordinate system.

#### 21.4 Plotting Curves Parallel to Axes

As there is no Plot function to draw a “straight line”, drawing axes and grid lines to represent coordinate systems requires a slightly different approach. The problem is that for some coordinate systems, these grid lines will not be geodesics, so astCurve and astPolyCurve (§21.3) cannot easily be used (you would have to resort to approximating grid lines by many small elements). Lines of constant celestial latitude provide an example of this, with the exception of the equator which is a geodesic.

The astGridLine function allows these curves to be drawn, as follows:

int axis;
double length;
...
astGridLine( plot, axis, start, length );

Here, “axis” specifies which physical coordinate axis we wish to draw parallel to. The “start” array contains the coordinates of the start of the curve and “length” specifies the distance to draw along the axis in physical coordinate space.

#### 21.5 Plotting Generalized Curves

We have seen how geodesic curves and grid lines can be drawn. The Plot class includes another method, astGenCurve, which allows curves of any form to be drawn. The caller supplies a Mapping which maps offset along the curve39 into the corresponding position in the current Frame of the Plot. astGenCurve, then takes care of Mapping these positions into graphics coordinates. The choice of exactly which positions along the curve are to be used to define the curve is also made by astGenCurve, using an adaptive algorithm which concentrates points around areas where the curve is bending sharply or is discontinuous in graphics coordinates.

The IntraMap class may be of particular use in this context since it allows you to code your own Mappings to do any transformation you choose.

#### 21.6 Clipping

Like many graphics systems, a Plot allows you to clip the graphics you produce. This means that plotting is restricted to certain regions of the plotting surface so that anything drawn outside these regions will not appear. All Plots automatically clip at the edges of the plotting area specified when the Plot is created. This means that graphics are ultimately restricted to the rectangular region of plotting space to which you have attached the Plot.

In addition to this, you may also specify lower and upper limits on each axis at which clipping should occur. This permits you to further restrict the plotting region. Moreover, you may attach these clipping limits to any of the Frames in the Plot. This allows you to place restrictions on where plotting will take place in either the physical coordinate system, the graphical coordinate system, or in any other coordinate system which is described by a Frame within the Plot.

For example, you could plot using equatorial coordinates and set up clipping limits in galactic coordinates. In general, you could set up arbitrary clipping regions by adding a new Frame to a Plot (in which clipping will be performed) and inter-relating this to the other Frames in a suitable way.

Clipping limits are defined using the astClip function, as follows:

#define NAXES 2
int iframe;
double lbnd[ NAXES ], ubnd[ NAXES ];
...
astClip( plot, iframe, lbnd, ubnd);

Here, the “iframe” value gives the index of the Frame within the Plot to which clipping is to be applied, while “lbnd” and “ubnd” give the limits on each axis of the selected Frame (NAXES is the number of axes in this Frame).

You can remove clipping by giving a value of AST__NOFRAME for “iframe”.

#### 21.7 Using a Plot as a Mapping

All Plots are also Mappings (just like the FrameSets from which they are derived), so can be used to transform coordinates.

Like FrameSets, the forward transformation of a Plot will convert coordinates between the base and current Frames (i.e. between graphical and physical coordinates). This would be useful if you were (say) reading a cursor position in graphical coordinates and needed to convert this into physical coordinates for display.

Conversely, a Plot’s inverse transformation converts between its current and base Frames (i.e. from physical coordinates to graphical coordinates). This transformation is applied automatically whenever plotting operations are carried out by AST functions. It may also be useful to apply it directly, however, if you wish to perform additional plotting operations (e.g. those provided by the native graphics system) at positions specified in physical coordinates.

There is, however, one important difference between using a FrameSet and a Plot to transform coordinates, and this is that clipping may be applied by a Plot (if it has been enabled using astClip—§21.6). Any point which lies within the clipped region of a Plot will, when transformed, yield coordinates with the value AST__BAD. If you wish to avoid this clipping, you should extract the relevant Mapping from the Plot (using astGetMapping) and use this, instead of the Plot, to transform the coordinates.

#### 21.8 Using a Plot as a Frame

Every Plot is also a Frame, so can be used to obtain the values of Frame attributes such as a Title, axis Labels, axis Units, etc., which are typically used when displaying data and/or coordinates. These attributes are, as for any FrameSet, derived from the current Frame of the Plot (§13.8). They are also used automatically when using the Plot to plot coordinate axes and coordinate grids (e.g. for labelling them—§21.12).

Because the current Frame of a Plot represents physical coordinates, any Frame operation applied to the Plot will effectively be working in this coordinate system. For example, the astDistance and astOffset functions will compute distances and offsets in physical coordinate space, while astFormat and astNorm will format physical coordinates in an appropriate way for display.

#### 21.9 Regions of Valid Physical Coordinates

When points in physical coordinate space are transformed by a Plot into graphics coordinates for plotting, they may not always yield valid coordinates, irrespective of any clipping being applied (§21.6). To indicate this, the resulting coordinate values will be set to the value AST__BAD (§5.9).

There are a number of reasons why this may occur, but typically it will be because physical coordinates only map on to a subset of the graphics coordinate space. This situation is commonly encountered with all-sky projections where, typically, the celestial sphere appears, when plotted, as a distorted shape (e.g. an ellipse) which does not entirely fill the graphics space. In some cases, there may even be multiple regions of valid and invalid physical coordinates.

When plotting is performed via a Plot, graphical output will only appear in the regions of valid physical coordinates. Nothing will appear where invalid coordinates occur. Such output is effectively clipped. If you wish to plot in these areas, you must change coordinate system and use, say, graphical coordinates to address the plotting surface directly.

#### 21.10 Plotting Borders

The astBorder function is provided to draw a (line) border around your graphical output. With most graphics systems, this would simply be a rectangular box around the plotting area. With a Plot, however, this boundary follows the edge of each region containing valid, unclipped physical coordinates (§21.9).

This means, for example, that if you were plotting an all-sky projection, this boundary would outline the perimeter of the celestial sphere when projected on to your plotting surface. Of course, if there is no clipping and all physical coordinates are valid, then you will get the traditional rectangular box. astBorder requires only a pointer to the Plot:

int holes;
...
holes = astBorder( plot );

It returns a boolean (integer) value to indicate if any invalid or clipped physical coordinates were found within the plotting area. If they were, it will draw around the valid unclipped regions and return a value of one. Otherwise, it will draw a simple rectangular border and return zero.

#### 21.11 Plotting Text

Using a Plot to draw text involves supplying a string of text to be displayed and a position in physical coordinates where the text is to appear. The position is transformed into graphical coordinates to determine where the text should appear on the plotting surface. You must also provide a 2-element “up” vector which gives the upward direction of the text in graphical coordinates. This allows text to be drawn at any angle.

Plotting is performed by astText, for example:

char text[ 21 ];
double pos[ NCOORD ];
float up[ 2 ] = { 0.0f, 1.0f };
...
astText( plot, text, pos, up, "TL" );

Here, “text” contains the string to be drawn, “pos” is an array of physical coordinates and “up” specifies the upward vector. In this case, the text will be drawn horizontally. The final argument specifies the text justification, here indicating that the top left corner of the text should appear at the position given.

Further control over the appearance of the text is possible by setting values for various Plot attributes, for example Colour, Font and Size. Sub-strings within the displayed text can be given different appearances, or turned into super-scripts or sub-scripts, by the inclusion of escape sequences (see section §21.13) within the supplied text string.

#### 21.12 Plotting a Grid

The most comprehensive plotting function available is astGrid, which can be used to draw labelled coordinate axes and, optionally, to overlay coordinate grids on the plotting area (Figure 8). The routine is straightforward to use, simply requiring a pointer to the Plot:

astGrid( plot );

It will draw both linear and curvilinear axes and grids, as required by the particular Plot. The appearance of the output can be modified in a wide variety of ways by setting various Plot attributes. The Label attributes of the current Frame are displayed as the axis labels in the grid, and the Title attribute as the plot title. Sub-strings within these strings can be given different appearances, or turned into super-scripts or sub-scripts, by the inclusion of escape sequences (see section §21.13) within the Label attributes.

#### 21.13 Controlling the Appearance of Sub-strings

Normally, each string of characters displayed using a Plot will be plotted so that all characters in the string have the same font size, colour, etc., specified by the appropriate attributes of the Plot. However, it is possible to include escape sequences within the text to modify the appearance of sub-strings. Escape sequences can be used to change, colour, font, size, width, to introduce extra horizontal space between characters, and to change the base line of characters (thus allowing super-scripts and sub-scripts to be created). See the entry for the Escape attribute in Appendix C for details.

As an example, if the character string “10\%^50%s70+0.5+” is plotted, it will be displayed as “1 0 0.5” - that is, with a super-scripted exponent. The exponent text will be 70% of the size of normal text (as determined by the Size attribute), and its baseline will be raised by 50% of the height of a normal character.

Such escape sequences can be used in the strings assigned to textual attributes of the Plot (such as the axis Labels), and may also be included in strings plotted using astText.

The Format attribute for the SkyAxis class includes the “g” option which will cause escape sequences to be included when formatting celestial positions so that super-script characters are used as delimiters for the various fields (a super-script “h” for hours, “m” for minutes, etc).

Note, the facility for interpreting escape sequences is only available if the graphics wrapper functions which provide the interface to the underlying graphics system support all the functions included in the grf.h file as of AST V3.2. Older grf interfaces may need to be extended by the addition of new functions before escape sequences can be interpretted.

#### 21.14 Producing Logarithmic Axes

In certain situations you may wish for one or both of the plotted axes to be displayed logarithmically rather than linearly. For instance, you may wish to do this when using a Plot to represent a spectrum of, say, flux against frequency. In this case, you can cause the frequency axis to be drawn logarithmically simply by setting the boolean LogPlot attribute for the frequency axis to a non-zero value. This causes several things to happen:

(1)
  The Mapping between the base Frame of the Plot (which represents the underlying graphics world coordinate system) and the base Frame of the FrameSet supplied when the Plot was created, is modified. By default, this mapping is linear on both axes, but setting LogPlot non-zero for an axis causes the Mapping to be modified so that it is logarithmic on the specified axis. This is only possible if the displayed section of the axis does not include the value zero (otherwise the attempt to set a new value for LogPlot is ignored,and it retains its default value of zero).
(2)
  The major tick marks drawn as part of the annotated coordinate grid are spaced logarithmically rather than linearly. That is, major axis values are chosen so that there is a constant ratio between adjacent tick mark values. This ratio is constrained to be a power of ten. The minor tick marks are drawn at linearly distributed points between the adjoining major tick values. Thus if a pair of adjacent major tick values are drawn at axis values 10.0 and 100.0, minor ticks will be placed at 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0 and 90.0 (note only 8 minor tick marks are drawn).
(3)
  If possible, numerical axis labels are shown as powers of ten. This depends on the facilities implemented by the graphics wrapper functions (see the next section). Extra functions were introduced to this set of wrapper functions at AST V3.2 which enable super-scripts and sub-scripts to be produced. Some older wrappers may not yet have implemented these functiosn and this will result in axis labels being drawn in usual scientific or decimal notation.

Whilst the LogPlot attribute can be used to control all three of the above facilities, it is possible to control them individually as well. The LogTicks and LogLabel attributes control the behaviour specified in items 2 and 3 above, but the default values for these attributes depend on the setting of the LogPlot attribute. This means that setting LogPlot non-zero will swicth all three facilites on, so long as zero values have not been assigned explicitly to LogTicks or LogLabel.

#### 21.15 Choosing a Graphics Package

The Plot class itself does not include any code for actually drawing on a graphics device. Instead, it requires a set of functions to be provided which it uses to draw the required graphics. These include functions to draw a straight line, draw a text string, etc. You may choose to provide functions from your favorite graphics package, or you can even write your own! To accomodate variations in the calling interfaces of different graphics packages, AST defines a standard interface for these routines. If this interface differs from the interface provided by your graphics package (which in general it will), then you must write a set of wrapper functions, which provide the interface expected by AST but which then call functions from your graphics package to provide the required functionality. AST comes with wrapper functions suitable for the PGPLOT graphics package (see SUN/15).

There are two ways of indicating which wrapper functions are to be used by the Plot class:

(1)
  A file containing C functions with pre-defined names can be written and linked with the application using options of the ??link

  command. (see §3.3 and Appendix E). AST is distributed with such a file (called `grf_pgplot.c`) which calls PGPLOT functions to implement the required functionality. This file can be used as a template for writing your own.

(2)
  The astGrfSet method of the Plot class can be used to “register” wrapper functions at run-time. This allows an application to switch between graphics systems if required. Graphics functions registered in this way do not need to have the pre-defined names used in the link-time method described above.

For details of the interfaces of the wrapper routines, see either the `grf_pgplot.c` file included in the AST source distribution, or the reference documentation for the astGrfSet method.

37Like any FrameSet, a Plot can be used as a Mapping. In this case it is the inverse transformation which is used when plotting (i.e. that which transforms between the current and base Frames).

38Remember, the physical coordinate space need not necessarily be 2-dimensional, even if the plotting surface is.

39normalized so that the start of the curve is at offset 0.0 and the end of the curve is at offset 1.0 - offset need not be linearly related to distance.

Copyright (C) 2021 East Asian Observatory