Skip to content

Latest commit

 

History

History
2833 lines (2393 loc) · 80.7 KB

File metadata and controls

2833 lines (2393 loc) · 80.7 KB

GPLOT examples and tutorial

Although both DIMFILM and GPLOT have "formal" manuals that are fairly complete, they don't really tell you how to do any specific task.

This document is an attempt at "task oriented" documentation that might be seen as a tutorial or at least a set of examples. These cover most of what GPLOT can do (DIMFILM can do some more things that aren't accessible from GPLOT).

The scripts that create the examples shown here also serve as a "test suite" (of sorts) for GPLOT and DIMFILM.

Here are links to each of the examples:

The simplest graph

Let's start by plotting a very simple graph. Normally, data to be plotted exists in disk files, but, in this first example, the data will be internally generated by the MEMTEST command, which dynamically allocates data arrays on NOS (static arrays are used on "Unix-like" systems) and fills X,Y values with a sine wave.

The script file (obey file) to generate this contains:

# SIMPLEST GRAPH
#
RESET
MEMTEST
TITLE "A*L VERY SIMPLE GRAPH"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE

Although very minimal, there are a few points worth noting.

  • For maximum NOS / "Unix" compatibility, it is best to use only UPPER CASE in obey files. If you use lower case, it will work on both systems, but the results may be different. NOS (in NORMAL mode) will internally convert lower case to upper case. So "A very simple graph" will appear exactly as that on "Unix", but as "A VERY SIMPLE GRAPH" on NOS. Using upper case and DIMFILM "string markup" is portable and handles subscripts, superscripts, fractions and so on too. Note that GPLOT should not be used in ASCII mode on NOS! GPLOT does not attempt to deal with 6/12 Display Code, but will convert it (mostly!) to upper case. However, internal arrays are not sized for 6/12 representations, so there will be problems (mainly strings being unexpectedly truncated rather than crashes, though).
  • The RESET command re-establishes a default state. If multiple obey files are used in one GPLOT session without using RESET at the top of each file, the results will be unpredictable (the state at the end on one obey file will be inherited by the next).
  • This script is device independent. It can be used "as is" with any of the supported devices. To generate SVG from it, we can use:
/ GPLOT or $ UGPLOT
? dev svg sveg 1200,800
? prefix obey-files
? obey obgraf1
? ex

which will create an SVG file called sveg001 (sveg001.svg on "Unix") using a "resolution" of 1200 by 800 pixels (with SVG, this is really a "size" rather than a resolution, as the vector data can be scaled without losing detail).

  • The various annotations associated with a graph (title, axis labels, etc.) should appear before the command which plots the data (XYLINE here).
  • The ranges of the X and Y axes are automatically determined from the supplied data.
  • The graph is drawn in a square area in the center of the output device "canvas". When plotting graphs, it is often better to fill the output canvas.
  • Everything is drawn in a single colour (red).

We will address these last two deficiencies in the next example.

Bounds Panes Devices and Coordinate systems in DIMFILM

It is perhaps worth understanding why the graph appears in a centered square area on the device even at this stage.

DIMFILM lets the user define a "world coordinate system" or "user coordinates". These "bounds" are defined by four numbers, xlo to xhi defining a range of X coordinates, and ylo to yhi defining a range of Y coordinates. This coordinate system is intended to be "square", in that a unit step in X should have the same length when drawn by the output device as a unit step in Y. The output devices each have their own ranges of valid coordinates. In the case of the interactive devices (GTerm and the Tektronix 4014), these ranges are fixed (not strictly true for GTerm). The valid range of coordinates can be set by the user for the "file" devices, SVG and EPS, by defining the "paper size" when the device is opened. The coordinate systems of these devices are also defined to be "square".

DIMFILM also has the concept of a "pane", which is initially identical to the "bounds". The "pane" is a rectangular subregion of the bounds, specified by four numbers in bounds coordinates.

For general purpose drawing, you want to have a "square" coordinate system, so that circles when drawn are circles and not ellipses.

For plotting graphs of data, though, "squareness" is not usually important. DIMFILM determines the locations and sizes of the various elements of a graph in terms of fractions of the pane. The graph will be placed inside the pane, squashed and stretched as may be to fill it.

The default initial bounds (and pane) are set to 0 to 1 by 0 to 1, which is a square region. To maintain "squareness" while maximising the use of the display area, the bounds region must cover either the height or the width of the display (or both).

To make the situation clearer, we can draw the limits of the device (which we have set to a 1200 by 800 "pixel" region when we opened the SVG device) and the outline of the bounds (and pane) region by adding these commands:

OUTLINE DEVICE
OUTLINE BOUNDS

The only way to use the full area of the display is for the bounds to have the same aspect ratio as the display. That is:

xhi - xlo     width_display
---------  =  -------------
yhi - ylo     height_display

NOS and Unix-like systems and compatibility

Before moving on, it may be worth discussing how GPLOT tries to maximise compatability between these two very different operating systems.

Perhaps the most obvious thing when glancing at the first example is that the script is entirely UPPER CASE. This need not be so, and the following will work on both "Unix-like" systems and NOS (although after transfer to NOS without using 6/12 coding - which should not be used - it will appear in upper case there).

# Simplest graph.
#
reset
memtest
title "A very simple graph"
xlabel "X axis"
ylabel "Y axis"
xyline
outline dev

On "Unix-like" systems, the output will be identical to the first script's output. However, on NOS, the title and labels will appear in upper case.

Using DIMFILM's "string markup sequences" (the *L here) allows lower case to be plotted on NOS.

If you really don't like using upper case for some reason, you can use lower case and "string markup" and get the same result as using upper case on both systems.

GPLOT also tries to use heuristics to make file name related matters transparent, although there are limits to this considering each NOS account has a flat file system with maximum 7 letter/digit upper case only file names!

For NOS / "Unix" compatibilty, you will have to stick to 7 character file names. However, you can specify a path for "Unix" that is prefixed to each file name using the PREFIX command. This prefix is not used for device output files, though, so they will be created in the current working directory.

If you don't care about NOS compatibility, you can use long file names, so long as the entire path name does not exceed 72 characters.

Note that NOS does not have file name extensions. In contrast, "Unix-like" systems pretty much rely on having file name extensions. EPS output files will be given .eps extensions on "Unix" and SVG files .svg. These should not be added to the file names you type in or put in scripts.

To help identify what files contain on NOS, I use the first two characters as a sort of "extension" -- e.g. OB for obey files -- but this is just a personal convention which NOS knows nothing about. Unfortunately, reserving three characters for a frame number on output files leaves only four characters for the name, and using two of those to distinguish EPS from SVG isn't a good idea.

GPLOT converts all supplied file names (including any PREFIX) to lower case on "Unix". You cannot use upper or mixed case file names in GPLOT on "Unix". This applies to all input and output files. Very rarely, this will be undesirable, but it almost always simplifies matters. On NOS, all file names are upper case and all lower case is converted to upper case by NOS.

Using different colours and filling the canvas

Here is the same graph as above, but with different classes of things in different colours and filling the output canvas.

The code for this is:

# USING COLOUR/STYLE GROUPS
#
RESET
GRAPHMODE ON
MEMTEST
CSGROUP GENERAL
COL 1 0 0
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0.1 1
TITLE "A*L SIMPLE GRAPH WITH BETTER COLOURS"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE
OUTLINE DEV

To use the full area of the device, the command:

GRAPHMODE ON

was added. This just sets the bounds to have the same aspect ratio as the display device internally. In this case, to match the aspect ratio of 1200 by 800 (3:2), the bounds will be set to 0 to 1.5 by 0 to 1.

Note that

GRAPHMODE OFF

resets the bounds to 0 to 1 by 0 to 1 and if bounds are explicitly specified, they just supersede the ones set by GRAPHMODE.

In GPLOT/DIMFILM there are 3 "different classes of things", namely:

  • Text (TEXT): this refers to all types of "strings".
  • Annotation (ANNOT): this refers to the various things that annotate graphs specifically.
  • Other (GENERAL): All elements not in the above classes. This includes all lines and points.

All of them are members of the class ALL.

These 3 classes are called "colour/style groups" and the current class is selected with the CSGROUP command. Following this, COLOUR and WIDTH commands set the colour (as normalised RGB) and line width for that CSGROUP only. Perhaps oddly, the line style (solid, dashed, etc.) is not set independently for different groups in DIMFILM. The current line style applies to all groups.

Note that any command can be abbreviated -- so long as the abbreviation remains unique, it will work. (Note that this is not true of evaluator operators, as we will see).

Adding a grid or graticule

In order to better see the values on plotted graphs, it is often useful to have a grid or graticule over which the graphs are plotted (as with traditional graph paper). This is easily added with the GRID command:

# ADD A GRID
#
RESET
GRAPHMODE ON
MEMTEST
CSGROUP GENERAL
COL 1 0 0
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0.1 1
TITLE "A*L SIMPLE GRAPH WITH A GRID"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
GRID BOTH
XYLINE
OUTLINE DEV

resulting in:

Changing the width of lines

As with colour, the width of lines can easily be set independently for each colour/style group with the WIDTH command.

# GRAPH PLOT WITH WIDER LINE
#
RESET
MEMTEST
GRAPHMODE ON
CSGROUP GENERAL
COL 1 0 0
WIDTH 3
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0.1 1
TITLE "A*L SIMPLE GRAPH WITH A WIDER LINE"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
GRID BOTH
XYLINE
OUTLINE DEV

Different graph styles

The graphs above are all drawn with the default "framed" graph style. An alternative is the "open" style:

# OPEN STYLE GRAPH
#
RESET
MEMTEST
GRAPHMODE ON
CSGROUP GENERAL
COL 1 0 0
WIDTH 3
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0.1 1
GSTYLE OPEN
TITLE "A*LN OPEN STYLE SIMPLE GRAPH"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE
OUTLINE DEV

Another alternative is the "axes" style:

# AXES STYLE GRAPH
#
RESET
MEMTEST
CSGROUP GENERAL
COL 1 0 0
WIDTH 3
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0.1 1
AXCUT 0.02 0
GSTYLE AXES
TITLE "A*LN AXES STYLE SIMPLE GRAPH"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE
OUTLINE DEV

To which a grid can be added in the obvious way:

# AXES STYLE GRAPH WITH GRID
#
RESET
GRAPHMODE ON
MEMTEST
CSGROUP GENERAL
COL 1 0 0
WIDTH 3
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0.1 1
AXCUT 0.02 0
GSTYLE AXES
GRID BOTH
TITLE "A*LN AXES STYLE SIMPLE GRAPH WITH GRID"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE
OUTLINE DEV

Multiple graphs on the same axes and "HERE" data

To plot more than one graph on the same axes is quite straightforward. First, note that we haven't explicitly specified the ranges of the axes in any of the graphs so far. Instead, the system has looked at the data and automatically found reasonable ranges for the axes. This is very often the best approach.

To plot more than one graph and keep automatically determining the axis ranges can be done with the with two commands: XYSAME and ANNOT OFF. The second prevents annotation being drawn multiple times and the first keeps the axis ranges automatically determined from the first data plotted.

GPLOT also lets you specify data "inline" in a script file. Here is a very simple example and the script that generated it.

# 2 CURVE GRAPH WITH INLINE DATA
#
RESET
#
GRAPHMODE ON
GSTYLE BOXED
#
CSGROUP GENERAL
COL 1 0 0
WIDTH 1
STYLE SOLID
#
CSG TEXT
COL 0 0 0
STYLE SOLID
#
CSG ANNOT
COL 0 0.1 1
STYLE SOLID
#
#--- DEFINE SOME DATA INLINE 
READ HERE 1 2
0 0
1 1
2 0
EOF
#--- DRAW THAT WITH AUTO DETERMINED AXIS RANGES
GRID BOTH
TITLE "T*LWO CURVES, SAME AXES, INLINE DATA"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE
#
#--- TURN OFF ANNOTATION AND KEEP AUTO DET RANGES FOR NEXT CURVE
ANNOT OFF 
XYSAME
#
#--- DEFINE SOME MORE DATA INLINE
READ HERE 1 2
0 1
1 0.1333
2 1
EOF
#
#--- DRAW THAT IN A DIFFERENT COLOUR AND WIDTH.
CSG GEN
COL 1 0 1
WIDTH 3
STYLE DASHDOT
XYLINE
OUTLINE DEVICE

Note the READ command. This is described further in the section on plotting data in files. For inline data, the keyword HERE is used with it, and the data follows immediately. That data is terminated by EOF. The numeric arguments to READ are "column numbers" and will almost always be 1 2 for inline data.

Multiple graphs and keys

GPLOT provides a straightforward (if inflexible) way of adding a key or legend to help identify the meaning of the lines (or points) on graphs.

Here is an example of three curves with a key and the generating script. The data is again defined by "HERE" data.

# 3 CURVE GRAPH WITH INLINE DATA AND KEYS
#
RESET
GRAPHMODE ON
#
GSTYLE BOXED
#
CSGROUP GENERAL
COL 1 0 0
WIDTH 1
STYLE SOLID
#
CSG TEXT
COL 0 0 0
STYLE SOLID
#
CSG ANNOT
COL 0 0.1 1
STYLE SOLID
#
# --- DEFINE SOME DATA INLINE 
READ HERE 1 2
0 0
1 1
2 0
EOF
#
# --- WE WILL ADD KEYS.
USEKEY
#
# --- DRAW THAT WITH AUTO DETERMINED AXIS RANGES
GRID BOTH
TITLE "T*LHREE CURVES WITH KEYS"
XLABEL "X*L AXIS"
YLABEL "Y*L AXIS"
XYLINE
ADDKEY "F*LIRST"
#
# --- TURN OFF ANNOTATION AND KEEP AUTO DET RANGES FOR NEXT CURVE
ANNOT OFF 
XYSAME
#
# --- DEFINE SOME MORE DATA INLINE
READ HERE 1 2
0 1
1 0.1333
2 1
EOF
#
# --- DRAW THAT IN A DIFFERENT COLOUR AND WIDTH.
CSG GEN
COL 1 0 1
WIDTH 3
STYLE DASHDOT
XYLINE
ADDKEY "S*LECOND"
#
# --- DEFINE YET MORE DATA INLINE
READ HERE 1 2
0 1
0.2 0.2
0.4 0.8
0.6 0.4
0.8 0.6
1.0 0.5
1.2 0.6
1.4 0.4
1.6 0.8
1.8 0.2
2 1
EOF
#
# --- DRAW THAT IN A DIFFERENT COLOUR AND WIDTH.
CSG GEN
COL 0.1 0.7 0.1
WIDTH 2
STYLE SOLID
XYLINE
ADDKEY "T*LHIRD"
#
# --- DRAW THE LEGEND.
KEYS
OUTLINE DEV

This allows adding keys for up to 20 curves (I find 10 curves on one graph is as much as I can cope with). The maximum length of the key text is also quite limited at a maximum of 15 characters (including any markup sequences).

The layout currently cannot be changed. The key/legend area is always positioned to the right of the graph. This may be better than trying to put the key on top of the graph, as it can be impossible to find a place to put that without obscuring part of the data (at least, I've often found that is the case over the decades). That approach could certainly be added, though.

Multiple curves with two different Y axis ranges

Sometimes it is useful to plot two (or more) curves with the same X axis but, because they have significantly different Y ranges, two distinct Y axes. This can be done by having one Y axis on the left of a graph and another on the right.

A simple example is:

# TWO CURVES, DIFFERENT AXIS RANGES
#
RESET
GRAPHMODE ON
CSG ANNOT
COL 0 0 1
CSG TEXT
COL 0 0 1
#
# --- GENERATE A SINE WAVE.
EVAL 0.1,TWPI,2,*,0.1,+,201,XLIN,X,SIN
#
# --- PLOT IT WITH AUTO AXIS RANGES.
XLAB "X"
YLABEL "S*LINE VALUE"
TITLE "T*LWO CURVES, DIFFERENT Y AXIS RANGES"
XYL
ANNOT OFF
#
# --- DEFINE A TRIANGLE WAVE SHAPE.
READ HERE 1 2
0 0
5 100
10 0
EOF
#
# --- PLOT THAT WITH AUTO AXIS, PUT VALUES ON RIGHT
CSG ALL
COLOUR 0 0 0
RIGHTANNOT ON
RYLABEL "R*LIGHT EDGE (TRIANGLE VALUES)"
XYL
OUTLINE DEV

This example includes the first use of the evaluator so far (to create the sine wave curve). It is not essential to this example, though.

Note that the axis ranges are still automatically determined (XYSAME is not used for obvious reasons). The ANNOT OFF command is need though, before the RIGHTANNOT command is used.

It is possible to create a key for such a graph too:

# TWO CURVES, DIFFERENT AXIS RANGES, KEY
#
RESET
GRAPHMODE ON
CSG ANNOT
COL 0 0 1
CSG TEXT
COL 0 0 1
#
# --- GENERATE A SINE WAVE.
EVAL 0.1,TWPI,2,*,0.1,+,201,XLIN,X,SIN
#
# --- WE WILL USE KEYS.
USEKEY
#
# --- PLOT IT WITH AUTO AXIS RANGES.
XLAB "X"
YLABEL "S*LINE VALUE"
TITLE "T*LWO CURVES, DIFFERENT Y AXIS RANGES"
XYL
ADDKEY "S*LINE"
ANNOT OFF
#
# --- DEFINE A TRIANGLE WAVE SHAPE.
READ HERE 1 2
0 0
5 100
10 0
EOF
#
# --- PLOT THAT WITH AUTO AXIS, PUT VALUES ON RIGHT
CSG ALL
COLOUR 0 0 0
RIGHTANNOT ON
RYLABEL "R*LIGHT EDGE (TRIANGLE VALUES)"
XYL
ADDKEY "T*LRIANGLE"
#
# --- DRAW LEGEND.
KEYS
OUTLINE DEV

Plotting data stored in a disk file

So far, we have plotted data that has been generated by GPLOT or stored as "HERE" data in the command stream. Very often, the data to be plotted will be in disk files, though.

GPLOT provides a very simple, albeit basic, way of reading such data and plotting it. Given a file containing the same number of numeric items on each line, with those items separated by spaces or a comma (and perhaps spaces), GPLOT can treat the n-th number on each line as an entry in a column of numbers. The columns are numbered 1, 2, 3, etc.

Any line that begins with # or C (upper case C, not lower case) will be skipped as a comment. Any entirely blank lines will also be skipped.

The READ command is followed by the name of the data file to be read then the column numbers for the X and Y coordinates.

Here is an example of some real measured data stored in a file (DAEG1) and plotted:

# READ AN X,Y DATA FILE AND PLOT THE CONTENTS
#
RESET
GRAPHMODE ON
#
# --- READ THE DATA FILE, FIRST 2 COLUMNS, SPACE OR COMMA SEP.
GET DAEG1
READ DAEG1 1 2
#
# --- DRAW THE GRAPH
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
TITLE "M*LAINS VOLTAGE ON 7-*UAUG*L-2025"
XLABEL "H*LOURS W.R.T MIDNIGHT 6/7 *UA*LUGUST"
YLABEL "V*LOLTS (*URMS - 220V *LNOMINAL)"
XYLINE
#
OUTLINE DEV

The data file will usually have at least two columns. However, a single column of data can be plotted. That data will become the Y values and the X values will be the line number (starting at 0). Use a 0 column number to do this. For example:

READ DAEG1 0 2

Labelling points on a graph

It is sometimes useful to point out something on a graph. Here is an example:

This is quite easy to do, in this case by adding the following commands between XYLINE and OUTLINE:

#
# --- ADD A LABEL OR TWO
CSG ALL
COL 0 0 0
GLABEL 9.8 192.0 0.1 165 "W*LHAT HAPPENED?"
CSG TEXT
COL 0.7 0.384 0.025
GLABEL 14 191.5 0.05 165 "E*LEK"

The GLABEL command's first two arguments are the graph coordinates to which the label's arrow should point. The third argument is the length of the arrow in bounds coordinates (which will be 0 to 1 by 0 to 1 by default, or 0 to (device aspect ratio) by 0 to 1 if GRAPHMODE ON is in effect). The fourth argument is the angle of the arrow around the location it is pointing at, counter-clockwise from +X in degrees. The final argument is the text for the label, which will be drawn in a box at the blunt end of the arrow.

Data with uncertainties in Y

If your data has an uncertainty measure associated with it, that can be plotted as an error bar on the graph. The meaning of this value is entirely up to you, of course. For common meanings see this Wikipedia article.

Using a concocted data file (DAEG2) which looks like this:

0.0,0.0,0.0
0.1,0.01,0.004
0.2,0.04,0.01
0.3,0.09,0.041
0.4,0.16,0.083
0.5,0.25,0.013
0.6,0.36,0.022
0.7,0.49,0.033
0.8,0.64,0.054
0.9,0.81,0.065
1.0,1.0,0.1

(note the use of comma separated instead of blank separated values here), the following script will plot a graph with error bar data taken from column 3:

# READ AN X,Y,E DATA FILE AND PLOT THE CONTENTS
#
RESET
GRAPHMODE ON
#
# --- READ THE DATA FILE, FIRST 3 COLUMNS. THIS ONE USES COMMAS.
GET DAEG2
READ DAEG2 1 2 3
#
# --- DRAW THE GRAPH
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
CSG GEN
COL 0.8 0.1 0.1
TITLE "L*LOOKS LIKE X*+2$+ WITH SYMMETRICAL ERRORS"
XLABEL "X"
YLABEL "Y"
XYLINE
OUTLINE DEV

The column given as the third argument to READ supplies the delta from the values taken from the column given as the second argument to READ at which error bar limits will be drawn above and below the value.

Data with asymmetric uncertainties in Y

It is possible that your data has potential deviations that are larger in the negative direction than in the positive direction, perhaps because the underlying data distribution is not normal, but skewed, or perhaps the device making measurements has errors that are proportional to the magnitude of the measured values. To visualise this, you need asymmetric error bars in Y.

To illustrate this, we have concocted another data file (DAEG3):

0.0,0.0,0.0,0.0,0.0
0.1,0.01,0.004,0.006,0.002
0.2,0.04,0.01,0.12,0.033
0.3,0.09,0.041,0.032,0.092
0.4,0.16,0.083,0.043,0.161
0.5,0.25,0.013,0.09,0.251
0.6,0.36,0.022,0.022,0.3617
0.7,0.49,0.033,0.021,0.5
0.8,0.64,0.054,0.066,0.666
0.9,0.81,0.065,0.032,0.814
1.0,1.0,0.1,0.05,0.997

Note this has 5 columns but here we will use the first four.

To plot this with asymmetrical error bars only requires a change to READ. The XYLINE (or XYPOINT) command will plot whatever the last READ has read.

Note the command: ASYMYERRORS YES which explicitly turns on this behaviour. This is not needed, however, as it is the default.

# READ AN X,Y,EU,EL DATA FILE AND PLOT THE CONTENTS
#
RESET
GRAPHMODE ON
#
# --- READ THE DATA FILE, FIRST 4 COLUMNS. THIS ONE USES COMMAS.
ASYMYERRORS YES
GET DAEG3
READ DAEG3 1 2 3 4
#
# --- DRAW THE GRAPH
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
CSG GEN
COL 0.8 0.1 0.1
TITLE "L*LOOKS LIKE X*+2$+ WITH ASYMMETRICAL ERRORS"
XLABEL "X"
YLABEL "Y"
XYLINE
OUTLINE DEV

This results in:

Note that the column specified as the 3rd argument to READ is the delta from the 2nd argument column value to the upper limit of the error bar, and the column specified as the 4th argument is the (negative) delta to the lower limit of the error bar.

It is easy to plot points on top of this. This might be useful when the points are derived from real measurements and a curve has been fitted to these measurements. As an example of this, we take the point Y coordinates from the 5th column of DAEG3 using this script:

# READ AN X,Y,EU,EL DATA FILE AND PLOT THE CONTENTS
#
RESET
GRAPHMODE ON
#
# --- READ THE DATA FILE, FIRST 3 COLUMNS. THIS ONE USES COMMAS.
ASYMYERRORS YES
GET DAEG3
READ DAEG3 1 2 3 4
#
# --- DRAW THE GRAPH
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
CSG GEN
COL 0.8 0.1 0.1
TITLE "*LX*+2$+ WITH ASYMMETRICAL ERRORS AND POINTS"
XLABEL "X"
YLABEL "Y"
XYLINE
#
# --- PLOT POINTS
XYSAME
ANNOT OFF
CSG GEN
COL 0 0 1
SYMHT 0.04
READ DAEG3 1 5
XYPOINT
#
OUTLINE DEV

Data with symmetric uncertainties in Y and X

It is also easy to plot data with uncertainty estimates in both axes. This requires the same 4 data columns as used above, but with the command:

ASYMYERRORS NO

Note that this must appear before the READ command, as it causes data to be rearranged internally immediately after it is read. In this case, the 3rd column is interpreted as symmetric Y error and the 4th as symmetric X error.

Interpolating the data points

The supplied data can be interpolated for the purposes of plotting "smooth" curves through, rather than straight lines between, the supplied data. This is generally frowned upon in science, but GPLOT/DIMFILM can do it!

In addition to LINEAR interpolation (which is usually pointless, as the appearance of the graph does not change), a cubic (3rd order) or quintic (5th order) polynomial can be drawn through the data points. At least 3 points must be supplied for cubic, and 5 for quintic, interpolation. The data must be sorted in X. The number of points to be found between each supplied point must be specified. If the data is not reasonably "well behaved", there can be numerical issues.

To demonstrate this capability, here is a very simple example:

This is created by the following script:

# INTERPOLATION
#
RESET
GRAPHMODE ON
#
# --- SOME DATA - VERY UNDERSAMPLED SIN(X)
READ HERE 1 2
0.00000  0.00000
0.78540  0.70711
1.57080  1.00000
2.35619  0.70711
3.14159  0.00000
3.92699 -0.70711
4.71239 -1.00000
5.49779 -0.70711
6.28319  0.00000
EOF
#
# --- SETUP THE GRAPH
USEKEY
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
CSG GEN
COL 0.8 0.1 0.1
YRANGE -1.2 1.2
TITLE "I*LNTERPOLATIONS"
XLABEL "X"
YLABEL "Y"
#
# --- LINEAR INTERPOLATION, 9 INTERMEDIATES.
INTERPOLATE LINEAR 9
XYLINE
ADDKEY "L*LINEAR"
#
# --- POINTS BEING INTERPOLATED.
XYSAME
MARKER 20
XYPOINT
#
# --- CUBIC
COL 0.1 0.8 0.1
INTERPOLATE CUBIC 9
XYLINE
ADDKEY "C*LUBIC"
#
# --- QUINTIC
COL 0.1 0.1 0.8
INTERPOLATE QUINTIC 9
XYLINE
ADDKEY "Q*LUINTIC"
#
# --- FINISH THE GRAPH
KEYS

Log Linear Plots

It is possible to use a logarithmic Y axis and linear X axis. An example of this is:

Well, if we were hoping for a straight line with log Y for this function, we are going to be disappointed! Here is the script for this:

# LOG Y AXIS
#
RESET
GRAPHMODE ON
CSG ANNOT
COL 0 0 1
CSG TEXT
COL 0 0 1
#
# --- SQUARES OF SOME NUMBERS.
READ HERE 1 2
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
10 100
EOF
#
# --- WE WILL USE KEYS.
USEKEY
#
# --- PLOT IT WITH LOG Y AXIS
XLAB "*LX"
YLABEL "*LLOG(Y)"
TITLE "*LY=X*+2$+ WITH LOG Y AXIS"
GRID BOTH
YLOG
XYLINE
ADDKEY "L*LOG"
ANNOT OFF
#
# --- PLOT IT WITH LINEAR Y AXIS, VALUES ON RIGHT
CSG ALL
COLOUR 0 0 0
RIGHTANNOT ON
RYLABEL "Y (L*LINEAR)"
YLIN
XYL
ADDKEY "L*LINEAR"
#
# --- DRAW LEGEND.
KEYS
#
# OUTLINE DEV

Let's try a power function instead:

Ah hah! This time we are in luck -- a straight line using a log-linear plot!

Log Log Plots

It is also easy to plot data with both log Y and log X axes. This script will do that with the same data as shown in the first log-linear plot (but with the values scaled up to see what happens):

# LOG X AND Y AXIS
#
RESET
GRAPHMODE ON
#
CSG ANNOT
COL 0 0 1
CSG TEXT
COL 0 0 1
#
# --- SQUARES OF SOME NUMBERS.
READ HERE 1 2
1.0E7 1
2.0E7 4
3.0E7 9
4.0E7 16
5.0E7 25
6.0E7 36
7.0E7 49
8.0E7 64
9.0E7 81
1.0E8 100
2.0E8 400
3.0E8 900
4.0E8 1600
5.0E8 2500
6.0E8 3600
7.0E8 4900
8.0E8 6400
9.0E8 8100
1.0E9 10000
EOF
#
# --- WE WILL USE KEYS.
USEKEY
#
# --- PLOT IT WITH LOG X AND Y AXES
XLAB "*LLOG(X)"
YLABEL "*LLOG(Y)"
TITLE "*LY=(*,X$,10*+7$+$.)*O*+2$+$* WITH LOG X AND Y AXES"
GRID BOTH
XLOG
YLOG
XYLINE
ADDKEY "L*LOG"
#
# --- PLOT IT WITH LINEAR Y AXIS, VALUES ON RIGHT
ANNOT OFF
CSG ALL
COLOUR 0 0 0
RIGHTANNOT ON
RYLABEL "Y (L*LINEAR)"
YLIN
XYL
ADDKEY "L*LINEAR"
#
# --- DRAW LEGEND.
KEYS

And we now have a straight line on the log-log plot for this function.

Note that one of the main reasons people plot experimental data with log-linear or log-log axes is in the hope of seeing something like a straight line when they do so, which gives them a clue to the form of a mathematical model which might "explain" the experimental data (or, at least, approximate it).

Histograms and multiple plots in one figure

Histograms are another common type of plot and GPLOT can draw them in various styles. There are no facilities for binning raw data so that histograms can be drawn, however. Other software must prepare counts of data points that fall into bins of specified ranges. All GPLOT deals with are the counts and bin numbers for each count.

As a minimal example, here is a single column of counts that can be represented by a histogram (file DAEG4):

100
123
90
20
10
1
200
233
129
43
77
6
2
9
64
99
12
18
111
87

The following script draws histogram plots of this data in four different styles:

# READ A COUNTS ONLY DATA FILE AND MAKE HISTOGRAMS.
#
RESET
#
# --- READ THE DATA FILE, FIRST COLUMN ONLY.
GET DAEG4
READ DAEG4 0 1
#
# --- SET COLOURS
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
CSG GEN
COL 0.8 0.1 0.1
#
# --- SET THE AXIS RANGES AND USE INT LABELS.
XRANGE -1 20
YRANGE 0 240
AXCUT NO
INTVALUES BOTH
#
# --- SET AXIS LABELS.
XLABEL "C*LLASS NUMBER"
YLABEL "C*LOUNT"
#
# --- ABUT STYLE IN BOTTOM LEFT.
PANE 0 0.5 0 0.5
HISTSTYLE ABUT
TITLE "C*LOUNTS HISTOGRAM (ABUTTING BARS)"
XYHIST
OUTLINE PANE
#
# --- SHADED ABUT STYLE IN BOTTOM RIGHT.
PANE 0.5 1 0 0.5
HISTSTYLE ABUT+SHADE
TITLE "C*LOUNTS HISTOGRAM (ABUTTING SHADED BARS)"
XYHIST
OUTLINE PANE
#
# --- SPECIFIED WIDTH BARS IN TOP LEFT.
PANE 0 0.5 0.5 1
HISTSTYLE WIDE 0.25
TITLE "C*LOUNTS HISTOGRAM (SPECIFIED WIDTH BARS)"
XYHIST
OUTLINE PANE
#
# --- THIN LINES IN TOP RIGHT.
PANE 0.5 1 0.5 1
HISTSTYLE LINES
TITLE "C*LOUNTS HISTOGRAM (IMPULSES)"
XYHIST
OUTLINE PANE
#
OUTLINE DEV

In addition to showing how to draw histogram plots, this example shows a number of other new features.

  • We explicitly specify X and Y axis ranges rather than letting the software choose. This is done with the XRANGE and YRANGE commands.
  • We prevent lines being drawn for the axes -- vertical and horizontal lines that pass through (0,0) (or another point specified with AXCUT) -- by using: AXCUT NO
  • We try to use integers for the axis values instead of floating point numbers using INTVALUES BOTH. This option can be requested for X, Y, both or neither axis. It may not always be honoured, as it will be impossible for many axis ranges. But in this case, it is a reasonable request and it works.

The most obvious new feature, though, is that the four plots showing the different histogram drawing styles appear in a single "figure".

This can be done easily with DIMFILM, as graphs are plotted inside any specified "pane" (see above), so all you have to do is use PANE appropriately.

Actually, though, that needs some (trivial) manual calculation, but it may also need to account for the device aspect ratio if the output canvas is to be filled. To further simplify creating multiple plots in a single "figure", GPLOT has the SUBFIGGRID command (which can be abbreviated, fortunately). This lets you specify a rectangular arrangement of "sub-figures" and the current sub-figure to be drawn (before any plotting commands) and calculates an appropriate pane for you, accounting for the device's aspect ratio (if GRAPHMODE ON has been used). It also lets you "shrink" the figure, so there is some white space between it and its neighbours.

An example of using this is:

The script for that is very similar to the one above, but with these changes:

# READ A COUNTS ONLY DATA FILE AND MAKE HISTOGRAMS.
#
RESET
GRAPHMODE ON
...
#
# --- ABUT STYLE IN BOTTOM LEFT.
SUBFIG 2 2 1 1 0.95
HISTSTYLE ABUT
...
#
# --- SHADED ABUT STYLE IN BOTTOM RIGHT.
SUBFIG 2 2 2 1 0.95
HISTSTYLE ABUT+SHADE
...
#
# --- SPECIFIED WIDTH BARS IN TOP LEFT.
SUBFIG 2 2 1 2 0.95
HISTSTYLE WIDE 0.25
...
#
# --- THIN LINES IN TOP RIGHT.
SUBFIG 2 2 2 2 0.95
HISTSTYLE LINES
...
#
OUTLINE DEV

Multiple plots with keys in one figure

The "sub-figures" feature also works with graphs with keys (some juggling of panes is involved internally). An example of this is:

This redraws four of the example plots shown above. Some minor changes to the scripts that drew them were made (any RESET, GRAPHMODE or other commands that changed the bounds or pane were removed). These modified versions are stored in the script files OBGF28A, OBGF28B, OBGF28C and OBGF28D. These are then "called" from a "master" script file OBGF28M (obey files can be nested up to 5 levels deep):

# FOUR DISPARATE PLOTS, ONE FIGURE.
#
#===========================
RESET
GRAPHMODE ON
# --- BOTTOM LEFT OF 4
SUBFIG 2 2 1 1 0.98
#
OBEY OBGF28A
#===========================
RESET
GRAPHMODE ON
# --- TOP RIGHT OF 4
SUBFIG 2 2 2 2 0.98
#
OBEY OBGF28B
#
# ========================
RESET
GRAPHMODE ON
# --- TOP LEFT OF 4
SUBFIG 2 2 1 2 0.98
#
OBEY OBGF28C
# ========================
# --- BOTTOM RIGHT OF 4
RESET
GRAPHMODE ON
SUBFIG 2 2 2 1 0.98
#
OBEY OBGF28D
OUTLINE PANE
#
OUTLINE DEV

(Note that files will be stored on "Unix-like" systems with lower case file names but may be referred to in upper case in GPLOT -- all file names are converted internally to lower case by GPLOT on "Unix" before being accessed. These names could appear in lower case -- as could all commands -- and it would work)

Please examine the "called" scripts for more information.

When generating a multi-plot "figure", you must keep the grid size constant in the SUBFIG command (e.g. SUBFIG 2 2) for every sub-figure. This is probably obvious, but nothing can check for this.

Note the repetition of RESET and GRAPHMODE ON for each sub-figure. Without this, the various colours and styles set in one sub-figure script would be inherited by the next. These must occur before the SUBFIG command, so they cannot be left in the script files.

Using the RPN evaluator to plot simple functions

GPLOT includes a feature for calculating function values rather than reading them from files. This is the Reverse Polish Notation (RPN) evaluator, which provides features very similar to those you would find on classic HP calculators.

One difference is that the RPN evaluator often operates on arrays rather than scalar values, in a way that might be familiar to people who have used NumPy or an APL-like language, perhaps. But with RPN thrown in for extra confusion!

The evaluator has quite a few features beyond those needed for simple function plotting, and its main limitation is perhaps that the "program" length is limited by the 80 character input line limit (beyond which all input is truncated).

A complete description of the available operators can be found in the "cheat sheet" below (which uses a notation familar to FORTH users) and in the PDF format GPLOT manual. Here, we just give some examples as an introduction.

Admittedly, the RPN evaluator may not be all that easy to use (the DUMP operator is helpful when debugging), but it opens up a lot of possibilities.

When dealing with data from files, GPLOT uses between 2 and 4 arrays to store the data to be plotted. Two are used to store X and Y coordinates. If symmetric error bars are to be plotted a third array is used to store that, and asymmetric Y error bars or Y and X error bars use a fourth array.

The RPN evaluator treats these four arrays as the first four levels of a stack. If READ is used, their contents will be overwritten by the read data and the array length seen by the evaluator will be the number of points read.

To provide "room" for any but the simplest calculations, another 4 stack levels (arrays) are provided "above" the four used for data from READ. (This can be configured with the NSTACK command).

It is possible to use the first two X and Y levels of the stack as inputs to the evaluator. More often, though, the contents of the X array are set to cover some range of values by the ERANGE command or certain evaluator operators, and the evaluator sets the Y values in the Y array. This is how it can fairly straightforwardly plot mathematical functions.

The evaluator "cheat sheet" below tries to show this stack of arrays diagrammatically.

As a first, very simple, example, let's compute "y=x^2". In previous examples the data has been precomputed and read from a file (often a "HERE" file). This script generates the Y values for a defined range of X values and plots the result (left hand subfigure, below).

RESET
GRAPHMODE ON
#
CSG ANNOT
COL 0 0 0
CSG TEXT
COL 0 0 0
#
ERANGE 1 1 100 100
EVAL X,2,**
#
XLABEL "X"
YLABEL "Y"
TITLE "X*+2"
GRID BOTH
XYLINE
#
OUTLINE DEV

The two commands that generate the data to be plotted are ERANGE and EVAL. The ERANGE command defines a range of X values for which the function is the be evaluated. In the case given here, it is similar to the NumPy "linspace" function. The first argument is the logarithm base, which, if 1, indicates we want to sample a linear range. The next two values are the start and end X values, and the final argument is the number of steps to take between start and end. ERANGE puts its output in the X array, which is also stack level 0.

The RPN evaluator does not use the X array as a "normal" part of the stack. It can be read via the X operator, which pushes its contents on the stack. The X array can be written to by the SETX operator. Otherwise, it is not accessed.

The EVAL operator needs one argument (a second is optional) which is an "RPN string" containing operators and operands for the evaluator to ... er ... evaluate. It may be enclosed in double quotation marks.

Whatever the evaluator leaves in the stack level 1 or Y array can be plotted against the X array contents using XYLINE or the other graphing commands.

To make the evaluator more useful, there are 9 "procedure registers" which can contain "RPN strings". These can be executed by the evaluator using the notation:

EVAL @1
EVAL @1,@3

which "calls" one or more procedure register contents (sequentially). The contents of a "procedure register" can be set with the PROC command. They can also be set with the LOADPROC command, which looks for a named procedure in the file GPLPROC and loads it into a specified "procedure register" if it is found.

Note that the GPLOT evaluator does not actually do a "call and return" when it sees EVAL @1, etc. Instead, it uses string substitution to replace @1 with the string in procedure register 1. It does this repeatedly, if necessary, until there are no remaining @n sequences in the substituted string.

There are also 9 scalar "memory" registers, which can be set with the STO command and recalled with RCL. Often, parameters for procedures are put into these registers with STO then accessed in the RPN with the RCL or (as an abbreviation) #.

To make evaluating procedures with parameters more straightforward, the optional second argument to EVAL is a comma separated list of numeric values to STO into consecutive scalar registers (starting with the 1st) before running the RPN string. For example,

EVAL @1 "1,2,3,4"

would put 1 in scalar register 1, 2 in register 2, and so on, then "call" the procedure in "procedure register" 1.

Finally, there are 9 "string registers" which can be set with arbitrary strings (maximum 80 characters) using the STRING command. These can then be used in an RPN procedure for plotting text.

Returning to plotting simple functions, this script graphs a slightly more complex function:

CSG ALL
COL 0 0 0
WIDTH 1
CSG GENERAL
COL 0 0 1
WIDTH 2
#
ERANGE 1 0 13 801
EVAL X,TWPI,*,SIN,X,PI,2,SQRT,*,*,SIN,+
#
XLABEL "*LX (ARGUMENT)"
YLABEL "*LY (NEVER REPEATS)"
TITLE "*LY=SIN(2 PI X) + SIN(2*+*,1$,2$.$+ PI X)"
XYL

This is plotted in the righthand sub-figure above. This function is mathematically interesting, as it never repeats (i.e. its period is infinite). This will not be the case when it is plotted on a digital computer, though, as all numbers on such machines are rational, but the period will be enormous.

General drawing Part 1 Basic functions

DIMFILM provides some basic drawing capabilities apart from its extensive graph plotting functionality. Graph plotting is based on these lower level capabilities, as you would expect.

There is a simple but sound foundation for general drawing with the BOUNDS command, which establishes a user defined coordinate system, the PANE command, which sets up a rectangle in bounds coordinates outside of which no drawing will occur (a "clip to inside" region) and the BLANK command, which sets a rectangle inside of which no drawing will occur (a "clip to outside" region). These commands map directly to DIMFILM subroutines.

For convenience, GPLOT adds a CANVAS command, which just sets the bounds and the pane to the same coordinate ranges.

All general purpose drawing uses the coordinates established by BOUNDS. If no explicit BOUNDS command is given, this defaults to 0 to 1 by 0 to 1, which will be a square region, centered in the output device drawable rectangle. As noted above, to use the full area of the output device, the aspect ratio of the bounds must match the aspect ratio of the device. This can be done automatically for graph plotting applications using GRAPHMODE ON, but you will generally want to choose your own coordinate ranges for general drawing, often bearing the device aspect ratio in mind when doing do.

The low level drawing operations available are MOVE to move the "current position" to a specified coordinate, DRAW to draw from the current postion to a new position (which becomes the current position) in a straight line, CIRCLE which draw a circle of a given radius at a given position, ARC which draws a segment of a circle, and RECT which draws a rectangle.

Two GPLOT commands build slightly on this: CRECT draws a rectangle centered on a given location and PATH uses the contents of the X and Y arrays to draw a polyline. DIMFILM does not support the drawing of any kinds of smooth curves for general drawing purposes (INTERPOLATE can interpolate smooth curves between data points for graph plotting, but this can't be used for general drawing).

As of V0.90, GPLOT adds two means of drawing curves: curves defined by a Bezier control polygon and a smooth curve that passes through supplied points (which works by calculating a full Bezier polygon such that the first and second derivatives of the resulting curve are continuous).

DIMFILM has very limited geometric transformation support and GPLOT does not provide access to this. GPLOT does provide a more comprehensive geometric transformation capability through its RPN evaluator, though.

While these graphics facilities are fairly basic, there is also a rather comprehensive set of facilities for drawing text using vector fonts. These are accessed through the TEXT and CTEXT commands, with various properties set by the SYMHT, SYMANG and FONT commands. The fonts are specific to DIMFILM (there is no way to use any "system fonts"!) and are, in this implementation, some of the Hershey fonts. The available fonts can be listed with the LISTFONT command.

Special "marker" characters can also be drawn using the MARKER command with the optional 2nd argument set to YES.

The text output facility allows "markup" to be included in the string to draw, and this extends to sub-scripts, super-scripts, and fractions, which can be nested to two levels. Mathematical notation can, to a useful degree, be accommodated by this. Although we have high quality font drawing everywhere now (and for the last few decades), when DIMFILM was first released (1973), this was cutting edge. Mathematical notation is still not readily expressible everywhere it might be useful even today, in fact.

An example which shows most of the basic drawing operations in action is:

The script for this is a little long, but may be worth presenting in full:

RESET
CANVAS 0 22 0 16
#
CSG TEXT
COL 0 0 0
CSG GEN
COL 0 0 0
#
# DRAW CIRCLES
CIRCLE 3 3 1
STYLE DASH
CIRCLE 19 3 1
STYLE DOT
COL 0 0 1
CIRCLE 19 13 1
STYLE DASHDOT
COL 1 0 0
CIRCLE 3 13 1
STYLE SOLID
COL 0 0 0
#
# DRAW ANGLE
MOVE 7 9
DRAW 10 9
MOVE 7 9
DRAW 9.5981 10.5
ARC 7 9 2 0 30
MOVE 9.45 9.5
SYMHT 0.5
TEXT "*:83=30"
#
# DRAW CENTER CROSS + MARKERS
COL 1 0 0
MOVE 2 8
DRAW 20 8
MOVE 11 2
DRAW 11 14
MOVE 11 8
COL 0 0 0
MARKER 9 Y
MARKER 23 Y
#
# DRAW CIRCLE CROSSES AND MARKERS
MOVE 1 13
DRAW 5 13
MOVE 3 11
DRAW 3 15
SYMHT 3
MOVE 3 13
MARKER 9 Y
#
MOVE 17 13
DRAW 21 13
MOVE 19 11
DRAW 19 15
MOVE 19 13
MARKER 24 Y
#
MOVE 1 3
DRAW 5 3
MOVE 3 1
DRAW 3 5
MOVE 3 3
MARKER 13 Y
#
MOVE 17 3
DRAW 21 3
MOVE 19 1
DRAW 19 5
MOVE 19 3
MARKER 3 Y
#
# DRAW ROUND CORNER RECTANGLE
WIDTH 3
ARC 7 6 1 180 270
ARC 15 6 1 270 360
ARC 7 10 1 90 180
ARC 15 10 1 0 90
MOVE 6 6
DRAW 6 10
MOVE 16 6
DRAW 16 10
MOVE 7 5
DRAW 15 5
MOVE 15 11
DRAW 7 11
WIDTH 1
#
# ODDITIES
MOVE 7 7
DRAW 8 6
DRAW 9 7
DRAW 10 6
#
MOVE 12 9
DRAW 13 10
DRAW 14 9
DRAW 15 10
#
READ HERE 1 2
12 6
14 6
15 7
12 7
EOF
PATH C
#
SYMHT 0.5
MOVE 11 15
CTEXT 0 "*=G*LENERAL DRAWING BASIC COMMANDS EXAMPLE$="
MOVE 11 1
CTEXT 12 "MOVE, DRAW, CIRCLE, ARC, MARKER, TEXT, CTEXT, PATH"
#
CSG GEN
COL 0 0 1
OUTLINE BOUNDS
OUTLINE DEV

Most of this is likely to be self explanatory. The only points that may be unclear are:

  • The use of a "special character" from the symbol font (which is loaded with MATH.SMALL by default) to draw the "theta" character. This is done by: TEXT "*:83=30" where the "theta" is selected by character number (83) using string markup. How do we know which character number to use? By looking at "font tables" which can be generated with one of the supplied script files (obfont for "normal" alphabets, obsyms for symbols and obmarks for markers). This is explained further in a later section (below).
  • The MARKER command, which will draw a specified marker (which is determined by a number) at the current position if a second argument (YES - abbreviated, any case) is supplied. If only one argument is supplied, the MARKER command sets the marker number to use for points when plotting graphs.
  • Polyline paths can be drawn with the PATH command with coordinates defined by a HERE file. This is quite a convenient way of setting up a path, actually. The path may be open or closed depending on the argument to PATH which may be C for closed or O for open.

While you can draw a lot with these basic drawing commands, it can get pretty tedious and long winded. Some of that is inevitable, but some things could be "automated". In this case, the end coordinate for the non-horizontal "angle" line has to be set by DRAW 9.5981 10.5 where those numbers were found by hand with a calculator! This is the sort of thing a computer ought to be able to do for you!

To help with this, in addition to simple evaluation of y = f(x) functions, the "evaluator" was added.

The RPN Evaluator for drawing 2D parametric functions

Bridging the graph plotting and general drawing worlds is drawing functions of a parameter, t, that trace out a path on the 2D plane. These are of the form:

x = f(t)
y = g(t)

Such functions can generate very pleasing patterns and often appear in "recreational mathematics". Some of the most familiar examples are the curves generated by the "Spirograph" "toy" (see this Wikipedia article), which we will come to shortly.

As a simpler example (from the point of view of GPLOT only!) we start with the "Farris mystery curve". The "mystery" is that a five-fold symmetry appears from formulae which contain mostly even numbers and no obvious source of such a symmetry.

While "Spirograph" uses two wheels, with one rotating in slip-free contact with the other, the Farris curves (many variations are possible) can be generated with three wheels in slip-free contact.

Here is the "original" mystery curve:

# FARRIS MYSTERY CURVE
#
RESET
CANVAS -2 2 -2 2
#
MAXPOINTS 3000
#
EVAL 0,TWPI,2001,XLIN
PROC 2 X,SIN,X,6,*,SIN,2,/,+,X,14,*,COS,3,/,+
PROC 1 X,COS,X,6,*,COS,2,/,+,X,14,*,SIN,3,/,+
EVAL @1,@2,PTHO
#
CSG ALL
COL 0 0 0
MOVE 0  -1.66
CTEXT 1.3 "*LX = COS(T) + *,COS(6T)$,2$. + *,SIN(14T)$,3$." 
MOVE 0  -1.9
CTEXT 1.3 "*LY = SIN(T) + *,SIN(6T)$,2$. + *,COS(14T)$,3$." 
MOVE 0, 1.9
CTEXT 2 "*IF*LARRIS MYSTERY CURVE"
#
OUTLINE BOUNDS

The script that generates this is quite short and simple. The first EVAL fills the X array (stack level 0) with 0 to 2 pi in 2001 linear steps. Two procedures are then defined (stored in procedure registers 1 and 2). PROC 1 calculates X values and PROC 2 finds Y values. The second EVAL evaluates these two procedures in the order that leaves the calculated coordinates on stack for use by the path drawing operator PTHO.

It is easy to plot this curve as a graph (i.e. with value labelled axes, etc.) simply by replacing PTHO with operators that set the calculated X and Y values into the X and Y arrays (stack levels 0 and 1) used by graph plotting commands (here XYLINE).

# FARRIS MYSTERY CURVE PLOTTED AS A GRAPH.
#
CANVAS -2 2 -2 2
#
MAXPOINTS 3000
CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0 1
CSG GEN
COL 1 0 0
#
EVAL 0,TWPI,2001,XLIN
PROC 2 X,SIN,X,6,*,SIN,2,/,+,X,14,*,COS,3,/,+
PROC 1 X,COS,X,6,*,COS,2,/,+,X,14,*,SIN,3,/,+
EVAL @1,@2,SETX,POP,SETY
GRAPHMODE OFF
TITLE "T*LHE *UF*LARRIS MYSTERY CURVE GRAPHED"
XLABEL "*LX = COS(T) + *,COS(6T)$,2$. + *,SIN(14T)$,3$."
YLABEL "*LY = SIN(T) + *,SIN(6T)$,2$. + *,COS(14T)$,3$."
GRID BOTH
XYLINE

The Spirograph is a toy invented by Denys Fisher in 1962-64 and fondly remembered by people of a certain age. It is a simple mechanism which can trace out hypotrochoid and epitrochoid curves based on two wheels (with gear teeth, so they can stay in contact without slipping). One wheel is fixed and the other rotates on the inside or outside of the fixed wheel. This second wheel has holes in which the tip of a pen can be placed to draw curves. If the moving wheel rotates inside the fixed wheel, a hypotrochoid curve is traced out, and if it is outside, an epitrochoid curve is drawn.

Much less accessible mechanisms were developed in the 19th century to draw such curves for use in banknote engraving as they are visually attractive and are (or were) difficult to forge without specialised machinery.

Drawing these curves with GPLOT is an interesting exercise. They can be defined with three integer parameters:

  • R, the radius of the fixed wheel.
  • RL, the radius of the smaller, moving, wheel.
  • P, the distance of the "pen hole" from the centre of the moving wheel.

We want to be able to change these easily, so we want to define a script that we can call like this:

OB OBSPIRO "65 25 30"

where we pass the values of R, RL and P to the script. This is a good opportunity to show how to pass parameters to scripts and to use "nested scripts" to reuse code in the way that subroutines and functions allow in more sophisticated programming environments.

The script OBSPIRO uses these parameters to calculate how many times the moving wheel needs to circle the fixed wheel to complete a closed curve, as well as the degree of symmetry of the curve. Note that the parameters passed to the script are referred to as $1, $2, etc. as they are in Unix scripting languages. Note also that it is possible to use parameters in the RPN strings used by EVAL. This can be very useful.

One "problem" with the parametrised Spirograph simulation is that we don't know how big the pattern will be -- i.e. what the range of coordinates will be -- so we can't easily set the BOUNDS for the drawing.

The OBSPIRO script deals with this using the BBSTART, BBEND and BBSET commands. Between BBSTART and BBEND, the DRAW command doesn't draw anything (all drawing ultimately comes down to MOVE and DRAW). Instead, the range of X and Y coordinates is accumulated. BBSET can then be used to set these accumulated ranges as the BOUNDS, optionally scaled about the center by a scale factor (here, 1.2).

The tracing out of the Spirograph is done by the script OBSPIRC, which OBSPIRO "calls" twice: once to find the pattern's bounds and once to draw the pattern.

In general, parameters can be passed to obey scripts in two ways:

  • Using parameters as described above, where, for example, OB X "1 2 3" results in $1, $2 and $3 in the script X being read as 1, 2 and 3 as a result of text (string) substitution.
  • Numerical values and strings can be passed through registers. This is necessary if values are calculated by EVAL to be used in a "nested" script.

The OBSPIRO script demonstrates all these features.

# SPIROGRAPH PATTERNS - OBSPIRO
#
# FOR MORE INFORMATION, SEE:
#   HTTPS://LINUXGAZETTE.NET/133/LUANA.HTML
#   HTTPS://APERIODICAL.COM/2021/12/THE-MATHEMATICS-OF-SPIROGRAPH/
#
RESET

# THE CONTROL PARAMETERS SHOULD ALL BE INTEGERS.
#
# $1 = R = BIG CIRCLE RADIUS
# $2 = RL = LITTLE CIRCLE RADIUS (MOVES AROUND BIG CIRCLE, IN CONTACT)
# $3 = P = LITTLE CIRCLE PEN RADIUS (FROM CENTRE OF LITTLE CIRCLE)

# THE DEGREE OF SYMMETRY OF THE PATTERN IS MAX(R/GCD(R,RL),RL/GCD(R,RL))

# THE NUMBER OF REVOLUTIONS AROUND THE BIG CIRCLE NEEDED TO JOIN
# UP TO THE START OF THE PATTERN IS:
# NR = RL / GCD(R,RL) (GCD=GREATEST COMMON DIVISOR)

# THE PARAMETER RANGE NEEDED TO DRAW THE THING IS THEN:
# T = [0:2*PI*NR]
#
# AND:
# X(T) = COS(T) * (R - RL) + COS(((R - RL) / RL) * T) * P
# Y(T) = SIN(T) * (R - RL) - SIN(((R - RL) / RL) * T) * P

# CALCULATE NR AND STORE IN REG 9. SAVE GCD(R,RL) IN REG 8.
# NOTE THE DOUBLE QUOTES ARE NEEDED FOR PARAMETER SUBSTITUTION TO WORK
EVAL "$2,$1,$2,GCD,8,=,/,9,="

# FIND THE DEGREE OF SYMMETRY. STORE IN REG 8.
EVAL "$1,8,#,/,$2,8,#,/,MAX,8,="

# FIND THE BOUNDING BOX AND SET BOUNDS.
BBSTART
OB OBSPIRC "$1 $2 $3"
BBEND
BBSET 1.2

# DRAW THE SPIROGRAPH
CSG GENERAL
COL 1 0 0
WIDTH 2
OB OBSPIRC "$1 $2 $3"

# DRAW ANNOTATION (OR NOT)
# CSG GENERAL
# COL 0 0 1
# WIDTH 2
# CIRCLE 0 0 $1
# EVAL "$1,$2,-,0,$2,C"
# EVAL "$1,$2,-,$3,+,0,$3,20,/,C"
# EVAL "$1,0,M,$1,$3,+,$2,-,$3,20,/,-,0,D"

# ADD A PARAMETER DISPLAY
CSG TEXT
COL 0 0 0
BOUNDS 0 1 0 1
MOVE 0.05 0.05
SYMHT 0.02
TEXT "R=$1, *LR=$2, P=$3"
STRING 9 "S*LYMMETRY="
STRING 8 "(I2)"
EVAL "0.05,0.1,M,9,T,TSC,8,#,8,TVI,TEC"
FONT 2 SCRIPT.SIMPLEX
MOVE 0.8 0.95
SYMHT 0.07
CTEXT 0 "*2S*LPIROGRAPH!"
FONT 2 SERIF.COMPLEX

CSG GEN
COL 0 0 0
OUTLINE BOUNDS

The OBSPIRC script does the actual work of calculating pattern coordinates and drawing the result.

# SPIROGRAPH PATTERN CORE - OBSPIRC

# $1 = R = BIG CIRCLE RADIUS
# $2 = RL = LITTLE CIRCLE RADIUS (MOVES AROUND BIG CIRCLE, IN CONTACT)
# $3 = P = LITTLE CIRCLE PEN RADIUS (FROM CENTRE OF LITTLE CIRCLE)
# REG 9 = NR = NUMBER OF REVOLUTIONS AROUND BIG CIRCLE
#
# T = [0:2*PI*NR]
#
# X(T) = COS(T) * (R - RL) + COS(((R - RL) / RL) * T) * P
# Y(T) = SIN(T) * (R - RL) - SIN(((R - RL) / RL) * T) * P

STO 1 $1
STO 2 $2
STO 3 $3
MAXPOINTS 3000

ERANGE 1 0 6.28 2001
EVAL 0,TWPI,2001,XLIN,X,9,RCL,*,SETX
EVAL 1,RCL,2,RCL,-,5,STO  ; [5] = R - RL
EVAL 5,RCL,2,RCL,/,6,STO  ; [6] = (R - RL)  / RL
PROC 1 X,COS,5,RCL,*,6,RCL,X,*,COS,3,RCL,*,+ ; X
PROC 2 X,SIN,5,RCL,*,6,RCL,X,*,SIN,3,RCL,*,- ; Y
EVAL @1,@2,PTHO

This is quite similar to the "Farris Mystery Curve" script in its approach. Note that it stores the parameters it receives in registers for use in the RPN, although direct substitution could be used. It first generates an angle parameter at 2001 points that "goes around" the required number of times to generate a closed pattern, then it finds the path coordinates based on the angle and draws the result with the open path operator, PTHO.

Here are some examples of the output of OBSPIRO with different parameter sets.

Still sticking with procedurally generated patterns, but moving away from traditional continuous functions of a variable, there is a traditional form of Japanese Sashiko stitching called Hitomezashi, developed in the Edo period.

These stitch patterns consist of short stitches of a single unit length, made either horizontally or vertically. If two random sequences of length N are generated, one for the horizontal direction and another for the vertical, and a stitch is made if the value of the sequence is odd (and not made if it is even) then a very attractive pattern results.

I came across this thanks to this Numberphile, video on YouTube, which gives an excellent explanation of things.

The following obey script draws such patterns:

# GENERATE HITOMEZASHI STITCH PATTERNS
#
RESET
CANVAS 0 41 0 41
BLANK 30.5 40.5 37.5 40.5

ERANGE 1 0 40 41
PROC 1 1,RAND,0.5,+,FLR,1,EL,X,+,ODD
EVAL 333,SEED

CSG GEN
COL 0 0 1
WIDTH 3
ITEVAL 0 40 1 0,I,M,@1,HDSH
ITEVAL 0 40 1 I,0,M,@1,VDSH

CSG ANNOT
COL 1 0 0
WIDTH 5
OUTLINE BLANK

UNBLANK
MOVE 35.5 39
CSG TEXT
COL 0 0 0
WIDTH 1
CTEXT 9 "H*LITOMEZASHI"

OUTLINE DEV

This introduces a new version of EVAL -- iterated evaluation or ITEVAL. This repeatedly runs an RPN procedure with a "loop variable" that iterates from a start value to a stop value (inclusive - FORTRAN DO-loop rules apply) by a step value. The "loop variable" value can be accessed inside the RPN using the I operator.

This example also shows how to use BLANK and UNBLANK to temporarily protect a rectangular region so that it cannot be drawn in.

General drawing Part 2 Evaluator assisted

It is possible to use the evaluator to assist with general drawing tasks. In many cases, it is necessary to calculate coordinates from a small set of supplied parameters in order to complete the drawing task. A good example of this was the "labelled angle line pair" shown in the manual general drawing section, where we used a calculator to find the coordinates of the label text literally by hand.

Probably the most convenient way of using the evaluator for such tasks is to add procedures to the evaluator procedure library which consists of the file GPLPROC (gplproc on Unix-like systems, with any specified PREFIX prepended).

The procedures in this library are, to some extent, a way to extend GPLOT's capabilities, or, at least, make some things much more convenient than they otherwise would be.

The contents of GPLPROC include comments that document its procedures. Please read it for full information. To understand its features, here are a couple of examples.

This RELLIPSE procedure draws an ellipse (which GPLOT/DIMFILM cannot do directly). It has five parameters, the center position (XC,YC), the semi-major and semi-minor axis lengths and a rotation angle (a rotation of the ellipse axes).

C A ROTATED ELLIPSE "XC YC A B ANGLE-DEGREES"
RELLIPSE
CL,0,TWPI,360,XLIN,X,&,COS,S,SIN,3,#,4,#,SCL,5,#,D2R,ROT,1,#,2,#,TRN,PTHC

To use this procedure to draw a rotated ellipse, two GPLOT commands are needed:

LOAD 1 RELLIPSE
EVAL @1 "5,1,0.5,0.9,33"

The first line loads the required procedure into a procedure register (1 here), then the second executes it, passing it the required arguments. If a given procedure is to be "called" multiple times, it need only be loaded once, of course.

The second example shows a useful feature unique to GPLPROC. A major limitation of the evaluator is that RPN strings are limited to 80 characters. This is not enough for many "real world" applications. When a procedure is loaded from GPLPROC, it can span multiple lines. In such a case, the procedure is loaded into multiple successive procedure registers and ,@n sequences are automatically added to line ends so that the when the procedure is called from an EVAL command, a single long procedure is assembled and executed. A side effect of this scheme is that lines can be no more than 77 characters long if another line follows in the procedure definition.

C GEAR "X Y R1 R2 N-TEETH ROT-DEGREES"
GEAR 2
CL,0,5,#,&,1,+,XLIN,3,#,4,#,X,2,/,ODD,SEL,X,TWPI,*,5,#,/,6,#,D2R,+,&
COS,S,SIN,3,G,*,S,3,G,*,1,#,2,#,TRN,PTHO

Note that the number of lines in the full procedure (2 here) must be given after the procedure name. These procedures must be loaded into a procedure register number that leaves enough successive registers to hold the full thing. Here, for example, LOAD 8 GEAR would work, but LOAD 9 GEAR would not.

The script OBPRTST calls every procedure currently defined in GPLPROC as shipped. The output of that is shown below.

It is intended that GPLOT users add their own procedures to GPLPROC to "extend" GPLOT to do things they want to do.

General drawing Part 3 Higher level drawing

Although the evaluator can "extend" the capabilities of GPLOT to some extent, it has some pretty severe limitations and inconveniences. The main limitations are the maximum size of procedures and the complete lack of branching and looping features. In practice, you cannot "draw anything" with GPLOT/DIMFILM in the way that you can with PostScript, for example.

There are some drawing operations beyond the (very) basic facilities described above which are sufficiently generally useful to "build in" to GPLOT. This hopefully makes it relatively easy to draw things that are commonly useful (easier than using PostScript, for example), while acknowledging that there are still fundamental limitations to what can be drawn.

To this end, GPLOT has three "higher level" drawing "primitives":

  • Decorated lines
  • Boxed text
  • Labels with pointing arrows.

These can be used for a broad range of diagrams, but especially block diagrams.

A decorated line is a series of connected and directed line segments, the ends of which may have:

  • Arrow heads. These will automatically point in the correct direction (hopefully).
  • "Half skips". These are (close to) quarter circles, and if a line ends and begins with one of these, it can give the appearence of skipping over another line, as was commonly seen in older circuit diagrams.
  • No decorations or "plain".

Such a line is drawn by the LINE command which takes a string that defines the line segments and their decorations. Each coordinate or point description defining a "vertex" in the "polyline" has the following components:

<decoration>x,y[,<annotation_string>]

<decoration> = P (plain) or A (arrow) or S (half skip)

<annotation_string> is a short string (currently limited to 5 characters).
It may contain DIMFILM string markup. It cannot contain spaces.

The annotation string, if supplied, will be drawn in a circle at the mid-point of the line segment ended by the point description that specifies it. This is intended to be a very short label.

The point descriptions are separated by ">" characters, which can be read in this context as "to".

Here is an example that defines a two segment decorated line, starting with no decoration and ending in an arrow head with a label, "Le", in the middle of the first of the two line segments:

LINE P7,6>P7,21,L*LE>A16,21

Additional characteristics of decorated lines are specified by the ARROWPARM and LINEPARM commands. The latter sets scale factors for drawing skips and annotations, while the former sets the type, size and appearance of arrow heads. Note that the width and colour of decorated lines are set in the usual way (lines, arrows and skips are in CSG GENERAL, annotation text in CSG TEXT).

Boxed text is intended to be a fairly flexible component for constructing block diagrams, tables and many other drawings. The element is basically text centered in a box of a specified size at a specified position (given either by centre or bottom left).

A number of other features can be added, though.

  • The inside of the box can be "filled" with hatched lines.
  • A second box, inside the first and surrounding the text, can be drawn. It is a BLANK region, and it can be outlined.
  • The text may have multiple lines (up to 5), separated by backslashes.
  • The text may be centered or left justified.
  • The text can contain DIMFILM string markup.
  • The position of the boxed text can be automatically "stepped" by specified X and Y "deltas" after eah one is drawn. This makes it much easier to use this for constructing tables and other things.

The main command for this element is BOXTEXT, which draws a given text string at a position given by either a supplied coordinate, or, if the coordinate is omitted, at the position of the last BOXTEXT incremented by previously specified deltas.

A number of secondary commands set current characteristics of BOXTEXT. These are:

  • BOXPSIZE which sets the box size and how the position coordinate is to be interpreted.
  • BOXPHATCH which sets the hatching parameters (or turns it off).
  • BOXPTEXT which sets how text is drawn inside the box. There are two distinct modes: FIX and SCALE. In FIX mode, the WIDTHSCALE parameter sets how many lines will fit in the box height, while in SCALE mode, it sets the fraction of the box width the text should be scaled to fill.
  • BOXPBOX which controls whether the outer and inner boxes are outlined.
  • BOXPDELTAS which sets the automatic position step after each BOXTEXT is drawn.

Labels are the same graphical entities used to label points on a graph, but the coordinates of the thing they point at is in bounds rather than graph coordinates.

Here is a simple example of how these facilities can be used to draw a small block diagram.

RESET
BOUNDS 0 42 0 26

CSG TEXT
COL 0 0 0
CSG ANNOT
COL 0 0 0
WID 2

BOXPSIZE 10 6 NO

BOXT "B*LOX 1" 7 3
BOXT "B*LOX 2" 21 3
BOXT "C*LURIOUS\*UB*LOX 3" 35 3

BOXT "B*LOX 4" 35 13

BOXPHATCH 0.05 8 HORIZ
BOXPBOX BOTH
BOXT "B*LOX 5\*UA*LN ODDITY" 21 21

CSG GEN
COL 0 0 0
ARROWPARM BARBED 0.8 2 0.3
LINEPARM 0.5 1.5
LINE P12,3>A16,3
LINE P26,3>A30,3
LINE P35,6>A35,10
LINE A35,16>P35,21,R*LT>A26,21
LINE P7,6>P7,21,L*LE>A16,21

LINE P12.5,12.5>P13.2,12.5>A14.1,14.1
ALABEL 13.2 12.5 2 300 "What is this thing?"

OUTLINE DEVICE

It may look painful to create a diagram such as this by entering coordinates, and perhaps it is. However, it may be less painful than it seems at first. A key simplification is to use an integer coordinate system with the box size a useful multiple of the unit size (e.g. so that middle of the box edges is easy to find). It is also helpful to have a grid printed out (!) with the coordinate system shown over a specified range (BOUNDS). As an aid to this, the script OBGRID can draw such a grid which can be used as the background of the diagram as it is developed (e.g. using the gterm device) or printed to help sketch out the diagram.

Surely it would be easier to just use, say, Inkscape? Perhaps. I have drawn many block diagrams with Inkscape and in some ways it is obviously easier. But it still takes longer than might be expected to get to a "final" result. It is also very difficult (at least for me) to get lines and boxes to "snap" together precisely (I usually give up on perfecting this, to be honest). The GPLOT approach at least results in exact alignment of all elements! In summary, I'm not sure it is all that more time consuming to get to an acceptable result. Which approach you prefer (using a mouse and relying on hand/eye coordination or working out and typing in coordinates) is a matter of personal taste. Obviously, if you want to draw a portrait of your dog, Inkscape (or a similar program) is the only sane choice! For more exactly defined tasks, a "language based" approach can make sense, as proved by the success of vector graphics description languages such as PGF/Tikz.

#
# DRAW A GRID WITH NUMBERED AXES. USEFUL FOR BLOCK DIAGRAMS.
# CALL WITH BOUNDS AS PARAMETERS.
#
BOUNDS $1 $2 $3 $4
CSG ALL
COL 0.8 0.8 0.8
STRING 9 "(I2)"
ITEVAL $3 $4 1 "$1,I,M,$2,I,D"
ITEVAL $1 $2 1 "I,$3,M,I,$4,D"
ITEVAL $3 $4 1 "$4,$3,-,100,/,TH,$1,I,M,I,9,TVI"
ITEVAL $1 $2 1 "$4,$3,-,100,/,TH,I,$3,0.5,+,M,I,9,TVI"

For a more sophisticated example of a block diagram drawn with GPLOT, look at the script OBMODIO which draws the block diagram at the top of this document showing the component parts of the Git-MODIFY inter-operability scheme.

An example of using GPLOT (primarily BOXTEXT) to produce a drawing other than a block diagram is shown below. This documents the key bindings for the NOS 2 Full Screen Editor with a Macbook laptop keyboard.

Further examples of how the "higher level drawing" features can be used can be seen in the OBCHT1 and OBCHT2 scripts which generate the GPLOT "cheat sheet" which can be found at the end of this document.

General drawing Part 4 Smooth curves

It is nice to be able to draw smooth curves in some cases, but DIMFILM itself has no facilities for this (except for interpolating points on graphs for data with monotonically increasing X).

Late in the day (V0.90), GPLOT added smooth curve drawing features. These are based on cubic Bezier curves.

Given that GPLOT is a text command based system, it isn't possible to interactively adjust "handles" on Bezier curves to get the shape you want. This is one reason smooth curves were omitted for a long time.

To help avoid this problem, one of the two smooth curve facilities generates a curve that passes through the points supplied. Internally, a Bezier curve is used, but the "handle" points (through which the curve does not pass) are calculated "behind the scenes" so that the curve has continuous first and second derivatives -- i.e. it is really smooth!

The points through which the curve passes are supplied in the same way as for plotting graphs and for the PATH command.

This script is a very simple example of the CURVE command with the open and closed boundary options. The points specified are shown with point markers so you can see the curve passes through them.

RESET
#
BOUNDS -1 5 -1 4
OB OBGRID "-1 5 -1 4"
#
READ HERE 1 2
0.0 1.0
3.0 1.0
2.0 3.0
1.5 0.0
EOF
# - DRAW POINT MARKERS
STRING 1 "(I2)"
COL 1 0 0
2DITEVAL 1 4 1 "X,I,EL,3,G,I,EL,3,G,S,0.3,TH,M,7,MK,I,1,TVI"
# - OPEN CURVE
COL 1 0 1
CURVE O
# - CLOSED CURVE
COL 0 0 1
CURVE C

The way the markers are drawn introduces a new way of calling the evaluator. The 2DITEVAL command works in the same way as the ITEVAL command, but preserves the contents of the second stack level which contains Y coordinates when data is READ.

In some cases, though, a perfectly smooth curve is not what is wanted. For this reason, a "plain" Bezier curve drawing command is also supplied. This takes a Bezier control polygon from the usual X and Y arrays. The curve will pass through every third point in the coordinate arrays. The two intermediate points are locations that "attract" the curve to them and are the points that are adjusted using "handles" in interactive systems. Many textbooks and online educational materials can be consulted for more details if desired. Note that the curve will only be "smooth" if the points before and after a point through which the curve passes, and that point, all lie on a straight line. Otherwise, more or less "sharp" changes in direction will occur -- which may be what is desired.

This script shows how to draw an open Bezier curve:

RESET
#
BOUNDS -1 21 -1 9
OB OBGRID "-1 21 -1 9"
#
READ HERE 1 2
0 4
2 8
9 3
10 2
13 0
19 3
20 7
EOF
#
# DRAW THE CONTROL POLYGON
COL 0.4 0.4 0.4
PATH O
#
# DRAW THE CONTROL POINTS
STRING 1 "(I2)"
COL 1 0 0
2DITEVAL 1 7 1 "X,I,EL,3,G,I,EL,3,G,S,0.3,TH,M,7,MK,I,1,TVI"
#
# DRAW AN OPEN BEZIER CURVE USING THIS CONTROL POLYGON.
BEZIER O

One thing which can be done with a closed Bezier curve is to draw an almost exact circle. This is an interesting test, in fact. A circle drawing script is:

RESET
#
BOUNDS -1.5 1.5 -1.5 1.5
OB OBGRID "-1.5 1.5 -1.5 1.5"
READ HERE 1 2
1 0
1 0.55228
0.55228 1
0 1
-0.55228 1
-1 0.55228
-1 0
-1 -0.55228
-0.55228 -1
0 -1
0.55228 -1
1 -0.55228
EOF
#
# DRAW THE CONTROL POINTS. WITH NUMERIC LABELS.
COL 1 0 0
STRING 1 "(I2)"
2DITEVAL 1 9 1 "X,I,EL,3,G,I,EL,3,G,S,0.2,TH,M,7,MK,0.1,TH,I,1,TVI"
STRING 1 "(I3)"
2DITEVAL 10 12 1 "X,I,EL,3,G,I,EL,3,G,S,0.2,TH,M,7,MK,0.1,TH,I,1,TVI"
#
# DRAW A CLOSED BEZIER CURVE THROUGH THESE POINTS. SHOULD BE A CIRCLE.
COL 0 0 1
BEZIER C

These smooth curve drawing facilities can also be accessed from the evaluator. Four operators have been added:

  • CRVO draws an open smooth curve through the points in the top two stack levels.
  • CRVC draws a closed smooth curve through those points.
  • BEZO draws an open Bezier curve using the control polygon in the top two stack levels.
  • BEZC draws a closed Bezier curve.

You must be careful with the number of points supplied to the BEZO and BEZC operators, as valid control polygons must have a specific number of points. An open Bezier curve control polygon must conform to ((N - 1) % 3) = 0, where N is the number of supplied points. A closed Bezier curve control polygon must conform to (N % 3) = 0. Here, % is the "modulo" or "remainder" operator.

These constraints also apply to the BEZIER command.

Here is a very simple script that shows these operators in use. This also used the 2DEVAL command to call the evaluator. Like 2DITEVAL, this preserves the contents of the array normally used to hold the Y values from a READ command.

RESET
#
BOUNDS -1 5 -1 5
#
READ HERE 1 2
0 1
3 1
2 3
1.5 0
EOF
#
COL 1 0 0
2DEVAL "X,S,CRVC"
#
READ HERE 1 2
0.0 4.0
0.0 1.0
1.0 0.0
4.0 0.0
EOF
#
COL 0 0 1
2DEVAL "X,S,BEZO"

L-Systems

L-systems or Lindenmeyer systems are a type of formal grammar (as in mathematical logic) developed by Aristid Lindenmeyer, a theoretical biologist, in 1968 to model the processes of plant development. They can be used to generate a variety of attractive images, beyond plants and trees, including self-similar fractals. See this Wikipedia article for more background information.

Lindenmeyer systems generating 2D vector graphics are included in GPLOT. The implementation is based on Paul Bourke's description of a 1991 commercial product (probably now long dead) which can be found here. As with many of Paul Bourke's web pages, this is very informative and inspirational.

L-systems work by manipulating strings, the characters of which either represent graphical actions (such as moving forward, drawing a line, or turning through an angle) or are "variables" with no associated graphical meaning.

An L-system is defined by an "axiom string" and a set of "re-writing rules", and, in our case, also a fixed "turning angle". The "axiom string" must be set in string register 1 and up to 8 "re-writing rules" can then appear in string registers 2 to 9 (used consecutively). The initial line angle is set by storing the angle (in degrees) in numeric register 3, with the X,Y coordinates of the origin of the system in registers 1 and 2.

The characters allowed ("vocabulary") are: F A B C D E M X Y + - [ ] Seven of these are associated with specific actions. Any alphabetic character can be substituted with a string by a re-writing rule. The seven "action characters" are:

  • F - draw a unit length line segment at the current drawing angle.
  • D - as F, but some systems need two substitutable drawing characters.
  • M - move by a unit length at the cureent drawing angle.
  • + - turn counter-clockwise by the turning angle.
  • - - turn clockwise by the turning angle.
  • [ - push the current position and drawing angle on a stack.
  • ] - pop the current position and drawing angle off a stack.

This is all very abstract, of course! Paul Bourke's web page gives a simple example which is quite easy to follow, but more complex systems are not obvious (to me, anyway).

Here is a classic example of a plant drawn with an L-system:

# PROCEDURE TO DRAW AN ALGORITHMICALLY DEFINED
# PLANT USING A LINDENMAYER SYSTEM.
# USAGE:
#  OBEY OBPLANT <ITERATIONS>
#   ITERATIONS BETWEEN 5 AND 8 SEEMS REASONABLE.

# INITIALISE GPLOT STATE.
RESET
GRAPHMODE ON

# SET THE COLOUR OF THE PLANT.
CSG GENERAL
COL 0.05 0.6 0.03

# DEFINE THE LINDENMAYER SYSTEM FOR THE PLANT.
STRING 1 X
STRING 2 F:FF
STRING 3 X:F-[[X]+X]+F[+FX]-X 
STO 3 90

# DRAW THE PLANT
LSYSTEM 2 $1 22.5

# SETUP TO ADD ANNOTATION
GRAPHMODE OFF
BOUNDS 0 1.33 0 1
CSG TEXT
COL 0 0 0

# DEMONSTRATE CTEXT CENTRED TEXT.
MOVE 0.3325 0.265
CTEXT 0.532 "L*LINDENMAYER *UP*LLANT"
MOVE 0.3325 0.185
CTEXT 0.4655 "X, F:FF, X:F-[[X]+X]+F[+FX]-X, 22.5*L DEGREES, $1 ITERATIONS."

# DEMONSTRATE ROTATED TEXT VIA THE RPN EVALUATOR.
STRING 5 "GPLOT/DIMFILM D*LEMONSTRATION AND TEST SCRIPT"
STRING 6 "  DIMFILM 1972-95, GPLOT 2013-"
EVAL "1.31,0.05,M,90,TA,5,T,1.31,0.6,M,6,T"

# ADD THE GPLOT VERSION INFO, DRAWING CENTRED TEXT
# VIA A STORED PROCEDURE.
VERSION 9
LOADP 1 ECTEXT
EVAL @1 "0.3325,0.125,0.45,9"

OUTLINE DEV

Note that, in general, the complexity of the drawing explodes as the number of iterations increases. The GPLOT L-system implementation uses scratch files to avoid memory limitations on NOS, so large outputs are possible, but the compute times also explode! The maximum stack depth is set to 20, although this doesn't seem to be much of a limitation in practice.

Here are some other examples.

A complicated, bristly plant:

RESET

# SET THE COLOUR OF THE PLANT.
CSG GENERAL
COL 0.05 0.6 0.03

# DEFINE THE LINDENMAYER SYSTEM FOR THE PLANT.
STRING 1 ABFFF
STRING 2 A:[+++C][---C]YA
STRING 3 C:+X[-C]B
STRING 4 X:-C[+X]B
STRING 5 Y:YB
STRING 6 B:[-FFF][+FFF]F

# INITIAL DRAWING ANGLE.
STO 3 90

# DRAW.
LSYSTEM 5 $1 20

A Minkowski island, after 4 iterations. L-systems can generate most (all?) fractal curves. Several such curves follow.

RESET

STRING 1 F+F+F+F
STRING 2 F:F+F-F-FF+F+F-F

STO 3 0

CSG GENERAL
COL 0.9 0 0.1

LSYSTEM 1 $1 90

A Sierpinski gasket after 6 iterations.

RESET

STRING 1 F+F+F        ; AXIOM
STRING 2 F:F-F+F+F-F  ; GENERATOR

STO 3 0               ; INITIAL ANGLE

CSG GENERAL
COL 0.9 0 0.1

# DRAW THE GASKET
LSYSTEM 1 $1 120

A Koch "snowflake" drawn with rectangular elements.

RESET

STRING 1 F+F+F+F
STRING 2 F:FF+F-F+F+FF

STO 3 0

CSG GENERAL
COL 0.9 0 0.1

LSYSTEM 1 $1 90

A Gosper curve, or "flowsnake", after 4 iterations.

RESET

STRING 1 F
STRING 2 F:F-D--D+F++FF+D-
STRING 3 D:+F-DD--D-F++F+D

STO 3 90

CSG GENERAL
COL 0.9 0 0.1

LSYSTEM 2 $1 60

GPLOT cheat sheet

GPLOT has quite a few commands and facilities. It is fully documented in a PDF format manual (LaTeX source for this can be found in the doc/gplot directory).

A quick guide in the form of a two page "cheat sheet" (drawn by GPLOT) is provided here, however. This is often sufficient as a reminder of the details when trying to do something.

Fonts

An example of each of the currently available fonts is shown in the figure below.

A complete set of font tables follow. You can use these to find the character number for any character in any font. These also show every character available in every font. Note there are some curious omissions ... for example, the default SANS.SIMPLEX font does not have an exclamation mark.

Alphabetic font tables

Symbol font tables

Marker font tables