ONLamp.com
oreilly.comSafari Books Online.Conferences.

advertisement


In Praise of Pic
Pages: 1, 2

If nothing else is specified, a position is located at the center of a picture element. However, we can also express different points on a picture element, by using compass points, such as .n (north), .ne (north-east), .e (east), etc. For lines, arrows, and splines, we can use the .start, .center, and .end control points. (The dot preceding each location name can be remembered by thinking of graphics elements as structs and the control points as their members.)



To specify arbitrary locations using these control points, we can either use offsets, using the notation of vector addition, e.g.,:

last box.ne + ( 0.1, 0.1 )

Alternatively, we can "interpolate" between any two positions by specifying a fraction, using the rather verbose syntax:

f of the way between p1 and p2

or the much terser variation (see Figure 6):

f< p1, p2 >.
.PS
C1: circle
move 
B:  box
move
C2: circle

arrow from C1 to B .nw + ( -0.1, 0 )         # Vector addition
arrow from C1 to B .sw + ( -0.1, 0 )

arrow from B to 1/3 between B .e and C2 .w   # Interpolation
.PE


Figure 6. Relative coordinates

In the last example, we have used labels (starting with a capital letter and ending with a colon) to refer to picture elements. Labels are a great help to stay in control of more complex diagrams. Use them a lot!

While relative coordinates are very convenient, and allow easy, almost logo-like creation of diagrams, absolute coordinates can have their advantages, too. In particular, I found it helpful to define a set of "reference points" at the beginning of a complex diagram and to assign labels to them. Relative coordinates and offsets can then be made from those reference points. Figure 7 illustrates absolute coordinates.

.PS
A: (    0, sqrt(3/4))
B: (  0.5, 0)
C: ( -0.5, 0)

line from A then to B then to C then to A
circle rad 0.1 at A
circle rad 0.1 at B
circle rad 0.1 at C
.PE


Figure 7. Absolute coordinates

Note the use of the mathematical expression sqrt(3/4) in the last example. Pic allows you to include mathematical expressions (including trigonometric functions, exponentials and logarithms) and to evaluate them at runtime.

Macros, Scripting, and Shell Escapes

You can define macros in pic, using the define command. Through macros, we can extend the very small set of built-in graphics primitives. The following program demonstrates a friendly example.

.PS
define smiley {
   # Takes three arguments: $1: x-pos, $2: y-pos, $3: size (radius)

   r0 = $3       # Face
   r1 = 0.4*r0   # Radius of mouth and eye locations
   r2 = 0.04*r0  # Radius of eyes

C: circle rad r0 at ( $1, $2 )

   circle rad r2 filled at last circle + ( r1, r1 )      # Right eye
   circle rad r2 filled at 2nd last circle + ( -r1, r1 ) # Left eye

   pi = atan2( 0, -1 )
S: C + ( r1*cos(1.25*pi), r1*sin(1.25*pi) )
   line from S to S
   for phi=1.25*pi to 1.75*pi by 0.1 do {
     line to C + ( r1*cos(phi), r1*sin(phi) )            # Mouth
   }
}

pi2 = 2*atan2( 0, -1 )
for x=0.1 to 1.3 by 0.08 do {
  smiley( 1.5*x*cos(x*pi2), 1.1*x*sin(x*pi2), 0.23*x ) 
}
.PE

Note how variables can be passed to the macro and are available inside the macro in the form of the expressions $1, $2, etc. See Figure 8.


Figure 8. Smiley

The last example also demonstrated pic's built-in looping facility. (There is a conditional construct, as well.)

Macros, together with the control flow constructs, can be used to generate graphics elements, which are missing from the list of built-in primitives. For instance, the mouth of the smiley in the example above shows how arbitrary arcs (i.e., with arbitrary start and end angles) can be set up as macros.

Finally, pic can execute an arbitrary command or include an arbitrary file through the sh or copy commands, respectively. This can be very handy. For instance, in the example below the Perl program trsf.pl applies a set of coordinate transformations to the raw coordinates contained in the file coord.raw and generates an intermediate file coord.tmp of pic objects (e.g., containing lines like A: ( 0.2, 0.5 ), etc.). This intermediate file is included though the copy command, and the locations defined in it are then used to generate the actual drawing.

.PS
sh { perl trsf.pl < coords.raw > coords.tmp }
copy "coords.tmp"

spline from A then to B then to C then to D then to E
circle at F
.PE

This can be more convenient than reading commands from standard input, because in the example above, the Perl program does not actually have to generate complete pic commands: it merely transforms and formats raw coordinates, the remainder of the drawing commands is kept in a separate pic program file.

(Note that escaping to a sub-shell is considered unsafe, since in principle any command could be executed by sh{...} and pic needs to be put into unsafe mode using the -U command-line option.)

After Pic

No program is an island, and pic is no exception. We have already seen how to include output of other programs into pic. What can we do with the output generated by pic?

Pic itself is merely a pre-processor for troff, so we can include troff commands in a pic program. In general, lines beginning with a period are being passed through to troff. This is mostly useful to control the rendering of text included in a pic-diagram. Some of the most relevant troff commands are:

.ft X
Changes the font shape, where X can be one of: R (upright, Roman), B (bold), I (italic), BI (bold italic), or P (previous, to switch back to the previous font).
.ps N
Changes the font size. The argument N can either be a number, giving the absolute size of the desired font in printer points (e.g., .ps 14), or it can be a relative size specification (such as .ps +2 or .ps -2), which changes the font size relative to the previous size. Omitting the argument will reset the font size to the previous value.
.fam F
Changes the font family, e.g., the argument T selects Times Roman, H selects Helvetica, etc.

By default, troff generates plain PostScript, although it can also generate output suitable for processing by TeX (as well as some other formats). One useful program to use with pic (and troff) is ps2eps. This program converts plain PostScript into encapsulated PostScript, and crops the page to the minimal bounding box containing the image. An encapsulated PostScript file so obtained can be converted using the convert utility from the ImageMagick toolset to practically any common graphics format out there , for instance to PNG, for use in web pages.

Summary

This concludes our brief overview of pic. Despite its age, it is still amazingly useful when generating certain diagrams, in particular when the diagrams are to be scripted in some way.

Pic is a small language and easy to pick up. Although this article doesn't cover everything, it introduces almost all of the elements of the pic language. The sensible choice of defaults and the rich facilities to express relative coordinates make working with pic quite easy and rather enjoyable.

The original tutorial and reference on pic was authored by Brian Kernighan: "PIC - A Graphics Language for Typesetting" and provides a complete introduction in a mere 25 pages. Eric S. Raymond has written an updated manual, specifically for the GNU version of pic (and groff) "Making Pictures with GNU PIC," which, at 37 pages, is not much longer. Between those two papers, you will find answers to almost all questions about pic. Both can be found on the Net.

The ps2eps tool is available at http://www.tm.uka.de/~bless/ps2eps and is highly recommended over the equivalent ps2epsi utility, which is part of the Ghostscript distribution. Finally, the homepage for ImageMagick's set of image manipulation tools is http://www.imagemagick.org.

Philipp K. Janert is a software project consultant, server programmer, and architect.


Return to ONLamp.com.



Sponsored by: