Next: Paths and Subpaths, Previous: C Compiling and Linking, Up: C Programming
The following is a sample application, written in C, that invokes GNU
libplot
operations to draw vector graphics. It draws an
intricate and beautiful path (Bill Gosper's “C” curve, discussed
as Item #135 in HAKMEM, MIT Artificial Intelligence Laboratory
Memo #239, 1972). As the numeric constant MAXORDER
(here
equal to 12) is increased, the path will take on the shape of a
curly letter “C”, which is the envelope of a myriad of epicyclic
octagons.
#include <stdio.h> #include <plot.h> #define MAXORDER 12 void draw_c_curve (plPlotter *plotter, double dx, double dy, int order) { if (order >= MAXORDER) /* continue path along (dx, dy) */ pl_fcontrel_r (plotter, dx, dy); else { draw_c_curve (plotter, 0.5 * (dx - dy), 0.5 * (dx + dy), order + 1); draw_c_curve (plotter, 0.5 * (dx + dy), 0.5 * (dy - dx), order + 1); } } int main () { plPlotter *plotter; plPlotterParams *plotter_params; /* set a Plotter parameter */ plotter_params = pl_newplparams (); pl_setplparam (plotter_params, "PAGESIZE", "letter"); /* create a Postscript Plotter that writes to standard output */ if ((plotter = pl_newpl_r ("ps", stdin, stdout, stderr, plotter_params)) == NULL) { fprintf (stderr, "Couldn't create Plotter\n"); return 1; } if (pl_openpl_r (plotter) < 0) /* open Plotter */ { fprintf (stderr, "Couldn't open Plotter\n"); return 1; } pl_fspace_r (plotter, 0.0, 0.0, 1000.0, 1000.0); /* set coor system */ pl_flinewidth_r (plotter, 0.25); /* set line thickness */ pl_pencolorname_r (plotter, "red"); /* use red pen */ pl_erase_r (plotter); /* erase graphics display */ pl_fmove_r (plotter, 600.0, 300.0); /* position the graphics cursor */ draw_c_curve (plotter, 0.0, 400.0, 0); if (pl_closepl_r (plotter) < 0) /* close Plotter */ { fprintf (stderr, "Couldn't close Plotter\n"); return 1; } if (pl_deletepl_r (plotter) < 0) /* delete Plotter */ { fprintf (stderr, "Couldn't delete Plotter\n"); return 1; } return 0; }
As you can see, this application begins by creating a
plPlotterParams
object to hold Plotter parameters, and sets the
PAGESIZE
parameter. It then calls the pl_newpl_r
function to create a Postscript Plotter. The Postscript Plotter will
produce output for a US letter-sized page, though any other standard
page size, e.g., "a4", could be substituted. This would be arranged by
altering the call to pl_setplparam
. The PAGESIZE
parameter is one of several Plotter parameters that an application
programmer may set. For a list, see Plotter Parameters.
After the Plotter is created, the application opens it and draws the
“C” curve recursively. The drawing of the curve is accomplished
by calling the pl_fmove_r
function to position the Plotter's
graphics cursor, and then calling draw_c_curve
. This subroutine
repeatedly calls pl_fcontrel_r
. The pl_fcontrel_r
function continues a path by adding a line segment to it. The
endpoint of each line segment is specified in relative floating point
coordinates, i.e., as a floating point offset from the previous cursor
position. After the “C” curve is drawn, the Plotter is closed by
calling pl_closepl_r
, which automatically invokes
pl_endpath_r
to end the path. A Postscript file is written
to standard output when pl_deletepl_r
is called to delete the
Plotter.
Specifying "png", "pnm", "gif", "svg", "ai", "cgm", "fig", "pcl",
"hpgl", "regis", "tek", or "meta" as the first argument in the call to
pl_newpl_r
, instead of "ps", would yield a Plotter that would
write graphics to standard output in the specified format, instead of
Postscript. The PAGESIZE
parameter is relevant to the "svg",
"ai", "cgm", "fig", "pcl", and "hpgl" output formats, but is ignored for
the others. Specifying "meta" as the Plotter type may be useful if you
wish to avoid recompilation for different output devices. Graphics
metafile output may be piped to the plot
utility and converted to
any other supported output format, or displayed in an X window.
See plot.
If "X" were specified as the first argument of pl_newpl_r
,
the curve would be drawn in a popped-up X window, and the output
stream argument would be ignored. Which X Window System display the
window would pop up on would be determined by the DISPLAY
parameter, or if that parameter were not set, by the DISPLAY
environment variable. The size of the X window would be determined
by the BITMAPSIZE
parameter, or if that parameter were not set,
by the BITMAPSIZE
environment variable. The default value is
"570x570". For the "png", "pnm", and "gif" Plotter types, the
interpretation of BITMAPSIZE
is similar.
You could also specify "Xdrawable" as the Plotter type. For you to make
this work, you would need to know a bit about X Window System
programming. You would need to create at least one X drawable
(i.e., window or a pixmap), and by invoking pl_setplparam
before
pl_newpl_r
is called, set it as the value of the parameter
XDRAWABLE_DRAWABLE1
or XDRAWABLE_DRAWABLE2
. For the
parameters that affect X Drawable Plotters, see Plotter Parameters.
The following is another sample application, written in C, that invokes
libplot
operations to draw vector graphics. It draws a
spiral consisting of elliptically boxed text strings, each of which
reads "GNU libplot!". This figure will be sent to standard output in
Postscript format.
#include <stdio.h> #include <plot.h> #include <math.h> #define SIZE 100.0 /* nominal size of user coordinate frame */ #define EXPAND 2.2 /* expansion factor for elliptical box */ void draw_boxed_string (plPlotter *plotter, char *s, double size, double angle) { double true_size, width; pl_ftextangle_r (plotter, angle); /* set text angle (degrees) */ true_size = pl_ffontsize_r (plotter, size); /* set font size */ width = pl_flabelwidth_r (plotter, s); /* compute width of string */ pl_fellipserel_r (plotter, 0.0, 0.0, EXPAND * 0.5 * width, EXPAND * 0.5 * true_size, angle); /* draw surrounding ellipse */ pl_alabel_r (plotter, 'c', 'c', s); /* draw centered text string */ } int main() { plPlotter *plotter; plPlotterParams *plotter_params; int i; /* set a Plotter parameter */ plotter_params = pl_newplparams (); pl_setplparam (plotter_params, "PAGESIZE", "letter"); /* create a Postscript Plotter that writes to standard output */ if ((plotter = pl_newpl_r ("ps", stdin, stdout, stderr, plotter_params)) == NULL) { fprintf (stderr, "Couldn't create Plotter\n"); return 1; } if (pl_openpl_r (plotter) < 0) /* open Plotter */ { fprintf (stderr, "Couldn't open Plotter\n"); return 1; } /* specify user coor system */ pl_fspace_r (plotter, -(SIZE), -(SIZE), SIZE, SIZE); pl_pencolorname_r (plotter, "blue"); /* use blue pen */ pl_fillcolorname_r (plotter, "white"); /* set white fill color */ pl_filltype_r (plotter, 1); /* fill ellipses with fill color */ /* choose a Postscript font */ pl_fontname_r (plotter, "NewCenturySchlbk-Roman"); for (i = 80; i > 1; i--) /* loop through angles */ { double theta, radius; theta = 0.5 * (double)i; /* theta is in radians */ radius = SIZE / pow (theta, 0.35); /* this yields a spiral */ pl_fmove_r (plotter, radius * cos (theta), radius * sin (theta)); draw_boxed_string (plotter, "GNU libplot!", 0.04 * radius, (180.0 * theta / M_PI) - 90.0); } if (pl_closepl_r (plotter) < 0) /* close Plotter */ { fprintf (stderr, "Couldn't close Plotter\n"); return 1; } if (pl_deletepl_r (plotter) < 0) /* delete Plotter */ { fprintf (stderr, "Couldn't delete Plotter\n"); return 1; } return 0; }
This example shows what is involved in plotting a text string or text
strings. First, the desired font must be retrieved. A font is
fully specified by calling pl_fontname_r
, pl_fontsize_r
,
and pl_textangle_r
, or their floating point counterparts
pl_ffontname_r
, pl_ffontsize_r
, and
pl_ftextangle_r
. Since these three functions may be called in
any order, each of them returns the size of the font that it selects, as
a convenience to the programmer. This may differ slightly from the size
specified in the most recent call to pl_fontsize_r
or
pl_ffontsize_r
, since many Plotters have only a limited repertory
of fonts. The above example plots each text string in the
"NewCenturySchlbk-Roman" font, which is available on Postscript
Plotters. See Text Fonts.
If you replace "ps" by "X" in the call to pl_newpl_r
, an X Plotter rather than a Postscript Plotter will be used, and the spiral
will be drawn in a popped-up X window. If your X display does
not support the "NewCenturySchlbk-Roman" font, you may substitute any
core X font, such as the widely available scalable font
"charter-medium-r-normal", or the traditional screen font "fixed".
For the format of font names, see Text Fonts in X. If the
X Plotter is unable to retrieve the font you specify, it will
first attempt to use a default scalable font ("Helvetica", interpreted
in the context of the X Window System as
"helvetica-medium-r-normal"), and if that fails, use a default Hershey
vector font ("HersheySerif") instead. Hershey fonts are constructed
from line segments, so each built-in Hershey font is available on all
types of Plotter.
If you are using an ancient (pre-X11R6) X Window System display, you will find that retrieving a font is a time-consuming operation. The above example may run slowly on such displays, since a new font must be retrieved before each text string is drawn. That is because each text string has a different angle of inclination. It is possible to retrieve individual characters from an X11R6 display, rather than retrieving an entire font. If this feature is available, the X Plotter will automatically take advantage of it to save time.