C-PLOT

Scientific Graphics and Data Analysis

filter programming

writing code for graphics filters

WRITING A GRAPHICS FILTER

In order to use C-PLOT with an unsupported graphics device, you will need to write some device-dependent code. All you have to do is supply the following functions and link your code with the module driver.o supplied with this package.

Almost all drawing, including text, is done by plotting lines. For drawing symbols as dots, a point routine is needed. For circular symbols (empty and filled), a circle routine is needed. The circle routine may also make use of a window (or clipping) routine to prevent circles near the edge of the plot from extending outside the axes.

For raster devices, such as dot matrix printers and memory-mapped displays, complete incremental line and circle drawing routines are available.

The arguments to all functions are integers.

openpl()
This routine is called before any plotting instructions are issued. Its sends mode-changing strings, clears buffers, opens temporary files or does whatever is needed to prepare the device for graphics. It must return a value of 0 if the open is successful. If, for instance, hardware is unavailable, the routine should return non-zero. This function will not be called again until after a closepl().
closepl(flag)
This routine should restore the device to text mode, send out the image to the printer or do whatever is necessary when the plot is complete. The argument flag is normally 0. It is nonzero when the filter is terminated abnormally (if the user hits ^C,for example, or if a termination signal was sent to the filter from another process).
dspage()
This routine is called when it is time to update the display. For instance, if the filter is using buffered output to a terminal, the buffer should be flushed. For many filters (all spooling filters, for example), this routine can do nothing.
sel_pen(x)
Although not required, you can provide a routine by this name. Its purpose is to select a line width or color on certain devices. The parameter x will either be zero or a positive integer and is simply the pen number a user gives as part of a p or z plotting command. You could interpret the parameter in any way. However, it is recommended you follow the conventions stated in the section that follows. If you don't provide a routine by this name, a dummy version will be loaded from libfilter.a.
erase()
This routine may erase the screen, clear raster buffers or do nothing, if that is appropriate.
space(x0,y0,x1,y1)
This routine sets up the scaling points for all subsequent drawing commands. The first two parameters are the coordinates of the lower left-hand corner. The second two are the coordinates of the upper right-hand corner. These coordinates should map to the entire available area of the device. All drawing command coordinates will lie within the region defined by these two points
point(x0,y0)
This routine is called to plot a point.
line(x0,y0,x1,y1)
This routine should draw a line between the points given by the two pairs of coordinates. It is not necessary to do any clipping as the endpoints are guaranteed to lie within the display area set up with space().
window(x0,y0,x1,y1)
This routine defines a clipping window. The first pair of arguments are the coordinates of the lower left-hand corner and the second pair are those for the upper right-hand corner. The clipping window is only required when drawing circles, and the only circles that are drawn are those used for circular plotting symbols. If you don't provide a window() routine, a do-nothing version will be loaded from libfilter.a.
circle(x0,y0,radius)
This function is called to draw a circle of radius radius with center at x0, y0. The circle should be clipped according to the current window. This routine is only called for plotting circular symbols. If the center of the circle is outside of the plotting window, this routine will not be called. If implementing a clipping window is too difficult, the result will be no worse than circular symbols overlapping the plot axes.
beg_sym(n) and end_sym()
These routines make it possible to replace the default grid fills for the solid symbols with device-dependent circle- and polygon-fill routines. When drawing points using one of C-PLOT's numbered special symbols, beg_sym() is called for each data point before any calls to point(), line() or circle(). The low byte of the argument n is one plus the symbol code as entered using the sy command. When the symbol is a black-filled symbol, the corresponding white-filled symbol outline is sent first, with 0x100 added to n to indicate the symbol is to be filled with black. When the lines for the grid fill are sent, beg_sym() is called again with 0x200 added to n. The routine end_sym() is called after all the function calls to point(),``line()`` and circle() required to draw each symbol have been made. Do-nothing versions of these two routines will be loaded from libfilter.a if they are not present in the source module you provide.

USING EXISTING Plot(5) ROUTINES

C-PLOT requirements differ in some places from the standard UNIX plot(5) function descriptions. In particular, the C-PLOT space() routine sets up a scaling region that encompasses the entire usable plotting area while the standard UNIX routine sets up a region that is the largest square that can fit on the device. Another difference is that some UNIX closepl() routines do not return the device to text mode. C-PLOT routines do.

In order to make it possible to use existing UNIX device-dependent plot libraries with C-PLOT, you can intercept the calls to several standard routines, adjust the arguments or execute some other code, before turning control over to them. The C-PLOT filter driver routines actually make the following function calls xopenpl(), xclosepl(), xspace(), xerase(), xpoint(), xline(), and xcircle() instead of the names given above. Normally, routines from libfilter.a are loaded that simply call routines by the x-less names.

The code shown here is an example of the interception trick that can be used to fix the problem with the scaling areas::

xspace(x0, y0, x1, y1) {
      long    x;

      x = x1 * 3L / 4L;
      space(x0, y0, (int) x, y1);
}

Here, a parameter provided to the UNIX library routine is rescaled so subsequent drawing commands will fill the entire screen.

This second example returns the device to text mode after calling the standard routine.:

closepl(mode) {
      closepl();
      /* Send sequence to reset text mode on this device */
      write(1, "\033[0g", 4);
}

The file g_proto.c in the filters subdirectory of the standard C-PLOT distribution has sample code that can be used to adapt existing plot(5) libraries to a C-PLOT filter.

SUBROUTINES IN libfilter.a

The following subroutines are tools to simplify writing filters. The first five routines interface to very fast and efficient code that buffers output written to the standard output (file descriptor 1).

binit()
Call this routine to initialize or reinitialize the internal buffer pointers.
bputch(c)
Adds the low eight bits of the integer c to the output queue.
bputwd(i)
Adds i as a short integer to the output queue, high byte first.
bputstr(s)
Adds the null-terminated string s to the output queue.
bwrite(p)
Adds n bytes of the data pointed to by the character pointer p to the output queue.
bflush()

Writes out any pending data in the queue.

These next routines are used with raster filters when you are not using the functions provided by raster.o. The first routine draws lines and the second circles. For both, the arguments must be already scaled to device units.

_line(x1,y1,x2,y2)
This routine draws raster lines. You must provide the following three routines, which are called by _line(),to set pixels on the device or in your raster buffer: _hline(x1,x2,y) draws a horizontal line from x1 to x2 at y, _vline(x,y1,y2) draws a vertical line from y1 to y2 at x and _ppoint(x,y) draws a point at x and y.
_circle(x,y,r)
This routine draws raster circles. You must assign values to the externally defined integers asx and asy if the pixel aspect ratio is not one to one. You must also provide the routine _ppoint(x,y) which draws a point at
window(x1,y1,x2,y2)
This routine is included with the above circle routines and sets the clipping window they use.

The following routines provide simple general purpose scaling.

space(x1,y1,x2,y2)
The coordinates are of the lower left and upper right corners of the maximum plotting area. The routine assigns values to the external variables _px0, _py0, _pdx and _pdy which are used in the following routines.
_mapx(x)
This routine returns the value in device units for x. You must declare and initialize the integer _xmax to contain the maximum value in device units for x. (The minimum value is assumed to be zero.) If you assign a nonzero value to the externally defined integer revx,the sense of the x axis is reversed. Make the assignment if the zero in device units for the device is on the right side of its long axis.
_mapy(y)
This routine returns the value in device units for y. You must declare and initialize the integer _ymax to contain the maximum value in device units for y. (The minimum value is assumed to be zero.) If you assign a nonzero value to the externally defined integer revy,the sense of the y axis is reversed. Make the assignment if the zero in device units for the device is on the top side of its short axis.
_mapr(r)
This routine uses the value of _xmax to provide a device-units value for the radius used in the circle() routine.

DEVICE-INDEPENDENT STREAM

You could write a complete filter without using any of the C-PLOT routines if you know the format of the data stream from the plot program and a few other details.

The data stream consists of single character commands, followed by zero to four parameters. The parameters are 16-bit signed integers, sent in binary fashion, with the low-order byte sent first.

The commands used are given in the following table.

Command Parameters Function
c 3 Draw circle
e 0 Erase display
W 0 Erase window
l 4 Draw line
p 2 Draw point
s 4 Set scaling points
w 4 Set clipping window
x 0 Open routine
y 0 Unsynchronized close routine
z 1 Update-display routine
B 1 Begin symbol
E 0 End symbol
P 1 Select pen
Y 1 Synchronized close routine
Z 1 Synchronized close routine
q 0 Flush output

The synchronized close routine must send a SIG_INT signal to the plot process when it has finished processing the graphics instructions. The parameter to the Y command is the process number (PID) of the plot process as a short integer (now obsolete). The parameter to the Z command is the process number (PID) of the plot process as a long integer.

If the filter is invoked with an argument, by convention the argument is the name of a file or device in which the filter should put the device dependent output.