Next: Drawing on a Page, Previous: Sample C Drawings, Up: C Programming
The most sophisticated sort of graphical object that libplot
can
draw is a path. In this section we explain the fine details of
constructing paths. The other three sorts of graphical object (text
strings, marker symbols, and points [i.e., pixels]) are discussed
elsewhere.
As in Postscript, paths may be simple or compound. A simple path is a contiguous sequence of line segments, circular arcs, elliptic arcs, quadratic Bezier curves, and/or cubic Bezier curves. A simple path may also be a circle, an ellipse, or a rectangle. A compound path consists of one or more simple paths, which must be nested: they should not intersect each other. This is more restrictive than in Postscript.
libplot
's drawing model is significantly different from
Postscript's, and is more user-friendly. Before drawing a path by
invoking libplot
operations, you do not need to call any special
function. You would specify the attributes of the path before drawing,
however. Attributes include pen color, line type, line width, cap type,
join type, and miter limit. If the path is to be filled, the fill
color and fill rule would be specified too. All these attributes are
`modal': their values are preserved from path to path.
In principle, you would end any path you construct, and request that it
be drawn on the graphics display, by invoking the endpath
operation. But endpath
is called automatically when any
path-related attribute is changed, when move
is called to change
the graphics cursor position, and before any other object is constructed
and drawn. It is also called at the end of each page of graphics,
i.e., when closepl
is invoked. So invoking endpath
explicitly is usually unnecessary. This is quite different from
Postscript, where an explicit command to stroke or fill a path is
required.
libplot
also differs from Postscript in the way it constructs and
draws compound paths. In libplot
, you would end each of the
constituent simple paths of a compound path by invoking the
endsubpath
operation. After all simple paths are drawn, the
compound path as a whole would be drawn by invoking endpath
.
After each of the calls to endsubpath
, you are allowed to call
move
to reposition the graphics cursor, prior to beginning the
next simple path. Immediately after an invocation of endsubpath
,
a call to move
will not automatically invoke endpath
.
The following sample program uses a Postscript Plotter to produce Postscript output. It draws a typical compound path, which consists of 17 simple paths. The first simple path is a large box. This box contains 7 circles, nested within each other, and a separate set of 7 circles that are also nested within each other. Within each of the two sets of nested circles is a pair of contiguous line segments, which make up an additional simple path. The compound path is drawn in green, and it is filled. The fill color is light blue.
#include <stdio.h> #include <plot.h> int main () { int i, j; 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 */ plotter = pl_newpl_r ("ps", stdin, stdout, stderr, plotter_params); /* open Plotter, i.e. begin a page of graphics */ pl_openpl_r (plotter); pl_fspace_r (plotter, 0.0, 0.0, 1000.0, 1000.0); /* set coor system */ pl_flinewidth_r (plotter, 5.0); /* set line thickness */ pl_pencolorname_r (plotter, "green"); pl_fillcolorname_r (plotter, "light blue"); pl_filltype_r (plotter, 1); /* do filling, full strength */ pl_erase_r (plotter); /* erase graphics display */ /* draw a compound path consisting of 17 simple paths */ /* draw the first simple path: a large box */ pl_orientation_r (plotter, 1); pl_fbox_r (plotter, 50.0, 50.0, 950.0, 950.0); pl_endsubpath_r (plotter); for (i = 0; i < 2; i++) /* draw 8 simple paths that are nested inside the box */ { /* first, draw 7 simple paths: nested circles */ for (j = 9; j >= 3; j--) { pl_orientation_r (plotter, j % 2 ? -1 : 1); pl_fcircle_r (plotter, 250.0 + 500 * i, 500.0, j * 20.0); pl_endsubpath_r (plotter); } /* draw an open simple path comprising two line segments */ pl_fmove_r (plotter, 225.0 + 500 * i, 475.0); pl_fcont_r (plotter, 250.0 + 500 * i, 525.0); pl_fcont_r (plotter, 275.0 + 500 * i, 475.0); pl_endsubpath_r (plotter); } /* formally end the compound path (not actually necessary) */ pl_endpath_r (plotter); /* close Plotter, i.e. end page of graphics */ pl_closepl_r (plotter); /* delete Plotter */ if (pl_deletepl_r (plotter) < 0) { fprintf (stderr, "Couldn't delete Plotter\n"); return 1; } return 0; }
As you will see if you run this program, the filling of the compound
path takes place in a visually pleasing way: alternating annular regions
are filled. That is because libplot
's default fill rule is
"even-odd". Since a compound path's constituent simple paths must
always be nested, it is easy for libplot
to determine which
regions between them are `even' and which are `odd'. It is the
latter that are filled.
The above program includes many invocations of orientation
. The
value of the modal `orientation' attribute (1, meaning
counterclockwise, or −1, meaning clockwise) applies to
subsequently drawn boxes, circles, and ellipses. If "even-odd" filling
is used, they have no effect. But if the fill rule for the compound
path is set to "nonzero-winding" by an initial call to fillmod
,
these calls to orientation
will arrange matters so that
alternating annular regions are filled, just as if "even-odd" filling
were used.
If the preceding paragraph is mysterious, it would be wise to consult a good book on Postscript programming, or any other reference on the subject of `winding numbers'.