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
- Bounds Panes Devices and Coordinate systems in DIMFILM
- NOS and Unix-like systems and compatibility
- Using different colours and filling the canvas
- Adding a grid or graticule
- Changing the width of lines
- Different graph styles
- Multiple graphs on the same axes and HERE data
- Multiple graphs and keys
- Multiple curves with two different Y axis ranges
- Plotting data stored in a disk file
- Labelling points on a graph
- Data with uncertainties in Y
- Data with asymmetric uncertainties in Y
- Data with symmetric uncertainties in Y and X
- Interpolating the data points
- Log Linear Plots
- Log Log Plots
- Histograms and multiple plots in one figure
- Multiple plots with keys in one figure
- Using the RPN evaluator to plot simple functions
- General drawing Part 1 Basic functions
- The RPN Evaluator for drawing 2D parametric functions
- General drawing Part 2 Evaluator assisted
- General drawing Part 3 Higher level drawing
- General drawing Part 4 Smooth curves
- L-Systems
- Alphabetic font tables
- Symbol font tables
- Marker font tables
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
NORMALmode) 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 inASCIImode 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
RESETcommand re-establishes a default state. If multiple obey files are used in one GPLOT session without usingRESETat 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
(
XYLINEhere). - 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.
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
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.
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).
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:
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
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
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.
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.
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
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
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.
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.
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
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.
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
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!
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 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
XRANGEandYRANGEcommands. - 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
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.
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.
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.SMALLby 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 (obfontfor "normal" alphabets,obsymsfor symbols andobmarksfor markers). This is explained further in a later section (below). - The
MARKERcommand, 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, theMARKERcommand sets the marker number to use for points when plotting graphs. - Polyline paths can be drawn with the
PATHcommand with coordinates defined by aHEREfile. This is quite a convenient way of setting up a path, actually. The path may be open or closed depending on the argument toPATHwhich may beCfor closed orOfor 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.
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,$2and$3in the scriptXbeing read as1,2and3as a result of text (string) substitution. - Numerical values and strings can be passed through registers. This is necessary
if values are calculated by
EVALto 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.
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.
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
BLANKregion, 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:
BOXPSIZEwhich sets the box size and how the position coordinate is to be interpreted.BOXPHATCHwhich sets the hatching parameters (or turns it off).BOXPTEXTwhich sets how text is drawn inside the box. There are two distinct modes:FIXandSCALE. InFIXmode, theWIDTHSCALEparameter sets how many lines will fit in the box height, while inSCALEmode, it sets the fraction of the box width the text should be scaled to fill.BOXPBOXwhich controls whether the outer and inner boxes are outlined.BOXPDELTASwhich sets the automatic position step after eachBOXTEXTis 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.
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:
CRVOdraws an open smooth curve through the points in the top two stack levels.CRVCdraws a closed smooth curve through those points.BEZOdraws an open Bezier curve using the control polygon in the top two stack levels.BEZCdraws 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 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- asF, 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 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.
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.