refer -e pic.man | pic -Pxxx | eqn -Pxxx | ditroff -ms -Pxxx
.TR 85 .so ~mac .ND "Revised Edition, March, 1982"
PIC \(em A Graphics Language for Typesetting

User Manual .AU "MH 2C-518" 6021 Brian W. Kernighan .AI .MH .AB

C PIC is a language for drawing simple figures on a typesetter. The basic objects in C PIC are boxes, circles, ellipses, lines, arrows, arcs, spline curves, and text. These may be placed anywhere, at positions specified absolutely or in terms of previous objects. The example below illustrates the general capabilities of the language.

S box invis wid .25 ellipse "document" arrow box "PIC" arrow box "TBL/EQN" "(optional)" dashed arrow box "TROFF" arrow ellipse "typesetter"

E This picture was created with the input

1 ellipse "document" arrow box "PIC" arrow box "TBL/EQN" "(optional)" dashed arrow box "TROFF" arrow ellipse "typesetter"

2

C PIC is another C TROFF processor; it passes most of its input through untouched, but translates commands between .PS and .PE into C TROFF commands that draw the pictures. .AE ....CS 15 4 19 0 0 3

Introduction

C PIC is a language for drawing simple pictures. It operates as yet another C TROFF .[ [ %r 54 %K CSTR %R Comp. Sci. Tech. Rep. No. 54 %I Bell Laboratories %C Murray Hill, New Jersey %A J. F. Ossanna %T N\s-2ROFF\s+2/T\s-2ROFF\s+2 User's Manual %K nroff troff %D October 1976 %J UNIX Programmer's Manual %V 2 %O Section 22 %I Bell Laboratories %C Murray Hill, N.J. %D January 1979 .]] preprocessor, (in the same style as C EQN .[ [ %A Brian W. Kernighan %A Lorinda L. Cherry %T A System for Typesetting Mathematics %J Communications of the ACM %V 18 %N 3 %D 1975 %P 151-157 %K cacm acm .]], C TBL .[ [ %r 49 %K CSTR %Q DNL %R Comp. Sci. Tech. Rep. No. 49 %I Bell Laboratories %C Murray Hill, New Jersey %A M. E. Lesk %T Tbl \(em A Program to Format Tables %D September 1976 %J UNIX Programmer's Manual %V 2 %O Section 10 %I Bell Laboratories %C Murray Hill, N.J. %D January 1979 .]] and C REFER .[ [ %r 69 %R Comp. Sci. Tech. Rep. No. 69 %K CSTR %Q DNL %A M. E. Lesk %T Some Applications of Inverted Indexes on the UNIX System %D 1978 %J UNIX Programmer's Manual %V 2 %O Section 11 %I Bell Laboratories %C Murray Hill, N.J. %D January 1979 .]]), with pictures marked by .PS and .PE .

C PIC was inspired partly by Chris Van Wyk's early work on C IDEAL .[ [ %A Christopher J. Van Wyk %A C. J. Van Wyk %T A Graphics Typesetting Language %J SIGPLAN Symposium on Text Manipulation %C Portland, Oregon %D June, 1981 .]]; it has somewhat the same capabilities, but quite a different flavor. In particular, C PIC is much more procedural\(ema picture is drawn by specifying (sometimes in painful detail) the motions that one goes through to draw it. Other direct influences include the C PICTURE language .[ [ %A John C. Beatty %T PICTURE \(em A picture-drawing language for the Trix/Red Report Editor %R Lawrence Livermore Laboratory Report UCID-30156 %D April 1977 .]] and the V viewgraph language .[ [ %A Anon. %T V \(em A viewgraph generating language %R Bell Laboratories internal memorandum %D May 1979 .]].

This paper is primarily a user's manual for C PIC ; a discussion of design issues and user experience may be found in .[ [ %A B. W. Kernighan %T PIC \(em A Language for Typesetting Graphics %J Software Practice & Experience %D January, 1982 %V 12 %N 1 %P 1-21 .]]. The next section shows how to use C PIC in the most simple way. Subsequent sections describe how to change the sizes of objects when the defaults are wrong, and how to change their positions when the standard positioning rules are wrong. An appendix describes the language succinctly and more or less precisely.

Basics

C PIC provides boxes, lines, arrows, circles, ellipses, arcs, and splines (arbitrary smooth curves), plus facilities for positioning and labeling them. The picture below shows all of the fundamental objects (except for splines) in their default sizes:

S box "box" move line "line" above move arrow "arrow" above move circle "circle" move ellipse "ellipse" move arc cw "arc"

E Each picture begins with .PS and ends with .PE ; between them are commands to describe the picture. Each command is typed on a line by itself. For example

1 .PS box "this is" "a box" .PE

2 creates a standard box (\(34 inch wide, \(12 inch high) and centers the two pieces of text in it:

S box "this is" "a box"

E

Each line of text is a separate quoted string. Quotes are mandatory, even if the text contains no blanks. (Of course there needn't be any text at all.) Each line will be printed in the current size and font, centered horizontally, and separated vertically by the current C TROFF line spacing.

C PIC does not center the drawing itself, but the default definitions of .PS and .PE in the -ms macro package do.

You can use circle or ellipse in place of box :

S circle "this is" "a box" move; move ellipse "this is" "a box"

E

Text is centered on lines and arrows; if there is more than one line of text, the lines are centered above and below:

1 .PS arrow "this is" "an arrow" .PE

2 produces

S arrow "this is" "an arrow"

E and

1 line "this is" "a line"

2 gives

S line "this is" "a line"

E

Boxes and lines may be dashed or dotted; just add the word dashed or dotted after box or line .

Arcs by default turn 90 degrees counterclockwise from the current direction; you can make them turn clockwise by saying arc cw . So

1 line; arc; arc cw; arrow

2 produces

S line; arc; arc cw; arrow

E A spline might well do this job better; we will return to that shortly.

As you might guess,

1 arc; arc; arc; arc

2 draws a circle, though not very efficiently.

Objects are normally drawn one after another, left to right, and connected at the obvious places. Thus the input

1 arrow; box "input"; arrow; box "process"; arrow; box "output"; arrow

2 produces the figure

S arrow; box "input"; arrow; box "process"; arrow; box "output"; arrow

E If you want to leave a space at some place, use move :

1 box; move; box; move; box

2 produces

S box; move; box; move; box

E

Notice that several commands can be put on a single line if they are separated by semicolons.

Although objects are normally connected left to right, this can be changed. If you specify a direction (as a separate object), subsequent objects will be joined in that direction. Thus

1 down; box; arrow; ellipse; arrow; circle

2 produces

S down; box; arrow; ellipse; arrow; circle

E and

1 left; box; arrow; ellipse; arrow; circle

2 produces

S left; box; arrow; ellipse; arrow; circle

E Each new picture begins going to the right.

Normally, figures are drawn at a fixed scale, with objects of a standard size. It is possible, however, to arrange that a figure be expanded to fit a particular width. If the .PS line contains a number, the drawing is forced to be that many inches wide, with the height scaled proportionately. Thus

1 .PS 3.5i

2 causes the picture to be 3.5 inches wide.

C PIC is pretty dumb about the size of text in relation to the size of boxes, circles, and so on. There is as yet no way to say ``make a box that just fits around this text'' or ``make this text fit inside this circle'' or ``draw a line as long as this text.'' All of these facilities are useful, so the limitations may go away in the fullness of time, but don't hold your breath. In the meantime, tight fitting of text can generally only be done by trial and error.

Speaking of errors, if you make a grammatical error in the way you describe a picture, C PIC will complain and try to indicate where. For example, the invalid input

1 box arrow box

2 will draw the message

1 pic: syntax error near line 5, file - context is box arrow ^ box

2 The caret ^ marks the place where the error was first noted; it typically T follows the word in error.

Controlling Sizes

This section deals with how to control the sizes of objects when the ``default'' sizes are not what is wanted. The next section deals with positioning them when the default positions are not right.

Each object that C PIC knows about (boxes, circles, etc.) has associated dimensions, like height, width, radius, and so on. By default, C PIC tries to choose sensible default values for these dimensions, so that simple pictures can be drawn with a minimum of fuss and bother. All of the figures and motions shown so far have been in their default sizes: box \(34\(fm\(fm wide \(mu \(12\(fm\(fm high circle \(12\(fm\(fm diameter ellipse \(34\(fm\(fm wide \(mu \(12\(fm\(fm high arc \(12\(fm\(fm radius line or arrow \(12\(fm\(fm long move \(12\(fm\(fm in the current direction

When necessary, you can make any object any size you want. For example, the input

1 box width 3i height 0.1i

2 draws a long, flat box

S box wid 3i ht 0.1i

E 3 inches wide and 1/10 inch high. There must be no space between the number and the i '' `` that indicates a measurement in inches. In fact, the i '' `` is optional; all positions and dimensions are taken to be in inches.

Giving an attribute like width changes only the one instance of the object. You can also change the default size for all objects of a particular type, as discussed later.

The attributes of height (which you can abbreviate to ht ) and width (or wid ) apply to boxes, circles, ellipses, and to the head on an arrow. The attributes of radius (or rad ) and diameter (or diam ) can be used for circles and arcs if they seem more natural.

Lines and arrows are most easily drawn by specifying the amount of motion from where one is right now, in terms of directions. Accordingly the words up , down , left and right and an optional distance can be attached to line , arrow , and move . For example,

1 .PS line up 1i right 2i arrow left 2i move left 0.1i line <-> down 1i "height" .PE

2 draws

S line up 1i right 2i arrow left 2i move left 0.1i line <-> down 1i "height"

E The notation <-> indicates a two-headed arrow; use -> for a head on the end and <- for one on the start. Lines and arrows are really the same thing; in fact, arrow is a synonym for line -> .

If you don't put any distance after up , down , etc., C PIC uses the standard distance. So

1 line up right; line down; line down left; line up

2 draws the parallelogram

S line up right; line down; line down left; line up

E

Warning: a very common error (which hints at a language defect) is to say

1 line 3i

2 A direction is needed:

1 line right 3i

2

Boxes and lines may be dotted or dashed:

S box dotted; line dotted; move; line dashed

E comes from

1 box dotted; line dotted; move; line dashed

2 If there is a number after dot , the dots will be that far apart. You can also control the size of the dashes (at least somewhat): if there is a length after the word dashed , the dashes will be that long, and the intervening spaces will be as close as possible to that size. So, for instance,

S line right 5i dashed

E

S line right 5i dashed 0.25i

E

S line right 5i dashed 0.5i

E

S line right 5i dashed 1i

E comes from the inputs (as separate pictures)

1 line right 5i dashed line right 5i dashed 0.25i line right 5i dashed 0.5i line right 5i dashed 1i

2

Sorry, but circles and arcs can't be dotted or dashed yet, and probably never will be.

You can make any object invisible by adding the word invis(ible) after it. This is particularly useful for positioning things correctly near text, as we will see later.

Text may be positioned on lines and arrows:

1 .PS arrow "on top of"; move arrow "above" "below"; move arrow "above" above; move arrow "below" below; move arrow "above" "on top of" "below" .PE

2 produces

S arrow "on top of"; move arrow "above" "below"; move arrow "above" above; move arrow "below" below; move arrow "above" "on top of" "below"

E

The ``width'' of an arrowhead is the distance across its tail; the ``height'' is the distance along the shaft. The arrowheads in this picture are default size.

As we said earlier, arcs go 90 degrees counterclockwise from where you are right now, and arc cw changes this to clockwise. The default radius is the same as for circles, but you can change it with the rad attribute. It is also easy to draw arcs between specific places; this will be described in the next section.

To put an arrowhead on an arc, use one of <- , -> or <-> .

In all cases, unless an explicit dimension for some object is specified, you will get the default size. If you want an object to have the same size as the previous one of that kind, add the word same . Thus in the set of boxes given by

1 down; box ht 0.2i wid 1.5i; move down 0.15i; box same; move same; box same

2

S down; box ht 0.2i wid 1.5i; move down 0.15i; box same; move same; box same

E the dimensions set by the first box are used several times; similarly, the amount of motion for the second move is the same as for the first one.

It is possible to change the default sizes of objects by assigning values to certain variables:

1 boxwid, boxht linewid, lineht dashwid circlerad arcrad ellipsewid, ellipseht movewid, moveht arrowwid, arrowht \f1(These refer to the arrowhead.)

2 So if you want all your boxes to be long and skinny, and relatively close together,

1 boxwid = 0.1i; boxht = 1i movewid = 0.2i box; move; box; move; box

2 gives

S x = boxwid; y = boxht boxwid = 0.1i; boxht = 1i movewid = 0.2i box; move; box; move; box boxwid = x; boxht = y

E

C PIC works internally in what it thinks are inches. Setting the variable scale to some value causes all dimensions to be scaled down by that value. Thus, for example, scale=2.54 causes dimensions to be interpreted as centimeters.

The number given as a width in the .PS line overrides the dimensions given in the picture; this can be used to force a picture to a particular size even when coordinates have been given in inches. Experience indicates that the easiest way to get a picture of the right size is to enter its dimensions in inches, then if necessary add a width to the .PS line.

Controlling Positions

You can place things anywhere you want; C PIC provides a variety of ways to talk about places. C PIC uses a standard Cartesian coordinate system, so any point or object has an T x and T y position. The first object is placed with its start at position 0,0 by default. The T x,y position of a box, circle or ellipse is its geometrical center; the position of a line or motion is its beginning; the position of an arc is the center of the corresponding circle.

Position modifiers like from , to , by and at are followed by an T x,y pair, and can be attached to boxes, circles, lines, motions, and so on, to specify or modify a position.

You can also use up , down , right , and left with line and move . Thus

1 .PS 2 box ht 0.2 wid 0.2 at 0,0 "1" move to 0.5,0 # or "move right 0.5" box "2" same # use same dimensions as last box move same # use same motion as before box "3" same .PE

2

draws three boxes, like this:

S 2 box ht 0.2 wid 0.2 at 0,0 "1" move to 0.5,0 # or "move right 0.5" box "2" same # use same dimensions as last box move same # use same motion as before box "3" same

E

Note the use of same to repeat the previous dimensions instead of reverting to the default values.

Comments can be used in pictures; they begin with a # and end at the end of the line.

Attributes like ht and wid and positions like at can be written out in any order. So

1 box ht 0.2 wid 0.2 at 0,0 box at 0,0 wid 0.2 ht 0.2 box ht 0.2 at 0,0 wid 0.2

2 are all equivalent, though the last is harder to read and thus less desirable.

The from and to attributes are particularly useful with arcs, to specify the endpoints. By default, arcs are drawn counterclockwise,

1 arc from 0.5i,0 to 0,0.5i

2 is the short arc and

1 arc at 2i,0 from 2i,0.5i to 2.5i,0

2 is the long one:

S arc from 0.5i, 0 to 0, 0.5i arc at 2i,0 from 2i,0.5i to 2.5i,0 circle rad .5i invis at 2i,0 # cheating

E If the from attribute is omitted, the arc starts where you are now and goes to the point given by to . The radius can be made large to provide flat arcs:

1 arc -> cw from 0,0 to 2i,0 rad 15i

2 produces

S arc -> cw from 0,0 to 2i,0 rad 15i

E

We said earlier that objects are normally connected left to right. This is an over-simplification. The truth is that objects are connected together in the direction specified by the most recent up , down , left or right (either alone or as part of some object). Thus, in

1 arrow left; box; arrow; circle; arrow

2 the left implies connection towards the left:

S arrow left; box; arrow; circle; arrow

E This could also be written as

1 left; arrow; box; arrow; circle; arrow

2

Objects are joined in the order determined by the last up , down , etc., with the entry point of the second object attached to the exit point of the first. Entry and exit points for boxes, circles and ellipses are on opposite sides, and the start and end of lines, motions and arcs. It's not entirely clear that this automatic connection and direction selection is the right design, but it seems to simplify many examples.

If a set of commands is enclosed in braces {...} , the current position and direction of motion when the group is finished will be exactly where it was when entered. Nothing else is restored. There is also a more general way to group objects, using [ and ] , which is discussed in a later section.

Labels and Corners

Objects can be labelled or named so that you can talk about them later. For example,

1 .PS Box1: box ... # ... other stuff ... move to Box1 .PE

2 Place names have to begin with an upper case letter (to distinguish them from variables, which begin with lower case letters). The name refers to the ``center'' of the object, which is the geometric center for most things. It's the beginning for lines and motions.

Other combinations also work:

1 line from Box1 to Box2 move to Box1 up 0.1 right 0.2 move to Box1 + 0.2,0.1 # same as previous line to Box1 - 0.5,0

2 The reserved name Here may be used to record the current position of some object, for example as

1 Box1: Here

2

Labels are variables \(em they can be reset several times in a single picture, so a line of the form

1 Box1: Box1 + 1i,1i

2 is perfectly legal.

You can also refer to previously drawn objects of each type, using the word last . For example, given the input

1 box "A"; circle "B"; box "C"

2 then last ` box ' refers to box C , last ` circle ' refers to circle B , and 2nd ` last box ' refers to box A . Numbering of objects can also be done from the beginning, so boxes A and C are 1st ` box ' and 2nd ` box ' respectively.

To cut down the need for explicit coordinates, most objects have ``corners'' named by compass points:

S 1.5 B: box "B.c" " B.e" at B.e ljust " B.ne" at B.ne ljust " B.se" at B.se ljust "B.s" at B.s below "B.n" at B.n above "B.sw " at B.sw rjust "B.w " at B.w rjust "B.nw " at B.nw rjust

E

The primary compass points may also be written as .r , .b , .l , and .t , for T right , T bottom , T left , and T top . The box above was produced with

1 .PS B: box "B.c" " B.e" at B.e ljust " B.ne" at B.ne ljust " B.se" at B.se ljust "B.s" at B.s below "B.n" at B.n above "B.sw " at B.sw rjust "B.w " at B.w rjust "B.nw " at B.nw rjust .PE

2 Note the use of ljust , rjust , above , and below to alter the default positioning of text, and of a blank with some strings to help space them away from a vertical line.

Lines and arrows have a start , an end and a center in addition to corners. (Arcs have only a center, a start, and an end.) There are a host of (i.e., too many) ways to talk about the corners of an object. Besides the compass points, almost any sensible combination of left , right , top , bottom , upper and lower will work. Furthermore, if you don't like the . ' ` notation, as in

1 last box.ne

2 you can instead say

1 upper right of last box

2 Prolixity like

1 line from upper left of 2nd last box to bottom of 3rd last ellipse

2 begins to wear after a while, but it is descriptive. This part of the language is probably fat that will get trimmed.

It is sometimes easiest to position objects by positioning some part of one at some part of another, for example the northwest corner of one at the southeast corner of another. The with attribute in C PIC permits this kind of positioning. For example,

1 box ht 0.75i wid 0.75i box ht 0.5i wid 0.5i with .sw at last box.se

2 produces

S box ht 0.75i wid 0.75i box ht 0.5i wid 0.5i with .sw at last box.se

E Notice that the corner after with is written .sw .

As another example, consider

1 ellipse; ellipse with .nw at last ellipse.se

2 which makes

S ellipse; ellipse with .nw at last ellipse.se

E

Sometimes it is desirable to have a line intersect a circle at a point which is not one of the eight compass points that C PIC knows about. In such cases, the proper visual effect can be obtained by using the attribute chop to chop off part of the line:

1 circle "a" circle "b" at 1st circle - (0.75i, 1i) circle "c" at 1st circle + (0.75i, -1i) line from 1st circle to 2nd circle chop line from 1st circle to 3rd circle chop

2 produces

S circle "a" circle "b" at 1st circle - (0.75i, 1i) circle "c" at 1st circle + (0.75i, -1i) line from 1st circle to 2nd circle chop line from 1st circle to 3rd circle chop

E By default the line is chopped by circlerad at each end. This may be changed:

1 line ... chop \f2r

2 chops both ends by \f2r, and

1 line ... chop \f2r1 chop \f2r2

2 chops the beginning by T r1 and the end by T r2 .

There is one other form of positioning that is sometimes useful, to refer to a point some fraction of the way between two other points. This can be expressed in C PIC as

1 \f2fraction of the way between \f2position1 and \f2position2

2 T fraction is any expression, and T position1 and T position2 are any positions. You can abbreviate this rather windy phrase; ``of the way'' is optional, and the whole thing can be written instead as

1 \f2fraction < \f2position1 , \f2position2 >

2 As an example,

1 box arrow right from 1/3 of the way between last box.ne and last box.se arrow right from 2/3 <last box.ne, last box.se>

2 produces

S box arrow right from 1/3 of the way between last box.ne and last box.se arrow right from 2/3 <last box.ne, last box.se>

E Naturally, the distance given by T fraction can be greater than 1 or less than 0.

Variables and Expressions

It's generally a bad idea to write everything in absolute coordinates if you are likely to change things. C PIC variables let you parameterize your picture:

1 a = 0.5; b = 1 box wid a ht b ellipse wid a/2 ht 1.5*b move to Box1 - (a/2, b/2)

2

Expressions may use the standard operators + , - , * , / , and % , and parentheses for grouping.

Probably the most important variables are the predefined ones for controlling the default sizes of objects, listed in Section 3. These may be set at any time in any picture, and retain their values until reset.

You can use the height, width, radius, and T x and T y coordinates of any object or corner in an expression:

1 Box1.x # the \f2x coordinate of Box1 Box1.ne.y # the \f2y coordinate of the northeast corner of Box1 Box1.wid # the width of Box1 Box1.ht # and its height 2nd last circle.rad # the radius of the 2nd last circle

2

Any pair of expressions enclosed in parentheses defines a position; furthermore such positions can be added or subtracted to yield new positions: delim $$ gsize 9 .EN

1 ( \f2x , \f2y ) ( $x sub 1$ , $y sub 1$ ) + ( $x sub 2$ , $y sub 2$ )

2 If $p sub 1$ and $p sub 2$ are positions, then

1 ( $p sub 1$ , $p sub 2$ )

2 refers to the point

1 ( $p sub 1$.x , $p sub 2$.y )

2 delim off gsize 10 .EN

More on Text

Normally, text is centered at the geometric center of the object it is associated with. The attribute ljust causes the left end to be at the specified point (which means that the text lies to the right of the specified place!), and rjust puts the right end at the place. above and below center the text one half line space in the given direction.

At the moment you can .ul not compound text attributes: however natural it might seem, it is illegal to say "..." above ljust . This will be fixed eventually.

Text is most often an attribute of some other object, but you can also have self-standing text:

1 "this is some text" at 1,2 ljust

2

Lines and Splines

A ``line'' may actually be a path, that is, it may consist of connected segments like this:

S line right 1i then down .5i left 1i then right 1i

E This line was produced by

1 line right 1i then down .5i left 1i then right 1i

2

A spline is a smooth curve guided by a set of straight lines just like the line above. It begins at the same place, ends at the same place, and in between is tangent to the mid-point of each guiding line. The syntax for a spline is identical to a (path) line except for using spline instead of line . Thus:

1 line dashed right 1i then down .5i left 1i then right 1i spline from start of last line \e right 1i then down .5i left 1i then right 1i

2 produces

S line dashed right 1i then down .5i left 1i then right 1i spline from start of last line\ right 1i then down .5i left 1i then right 1i

E (Long input lines can be split by ending each piece with a backslash.)

The elements of a path, whether for line or spline, are specified as a series of points, either in absolute terms or by up , down , etc. If necessary to disambiguate, the word then can be used to separate components, as in

1 spline right then up then left then up

2 which is not the same as

1 spline right up left up

2

At the moment, arrowheads may only be put on the ends of a line or spline; splines may not be dotted or dashed.

Blocks

Any sequence of C PIC statements may be enclosed in brackets [...] to form a block, which can then be treated as a single object, and manipulated rather like an ordinary box. For example, if we say

1 box "1" [ box "2"; arrow "3" above; box "4" ] with .n at last box.s - (0,0.1) "thing" at last [].s

2 we get

S box "1" [ box "2"; arrow "3" above; box "4" ] with .n at last box.s - (0,0.1) "thing" at last [].s

E Notice that ``last''-type constructs treat blocks as a unit and don't look inside for objects: last "" `` box.s '' refers to box 1, not box 2 or 4. You can use last [] , etc., just like last box .

Blocks have the same compass corners as boxes (determined by the bounding box). It is also possible to position a block by placing either an absolute coordinate (like 0,0 ) or an internal label (like A ) at some external point, as in

1 [ ...; A: ...; ... ] with .A at ...

2

Blocks join with other things like boxes do (i.e., at the center of the appropriate side). It's not clear that this is the right thing to do, so it may change.

Names of variables and places within a block are local to that block, and thus do not affect variables and places of the same name outside. You can get at the internal place names with constructs like

1 last [].A

2 or

1 B.A

2 where B is a name attached to a block like so:

1 B : [ ... ; A: ...; ]

2 When combined with define statements (next section), blocks provide a reasonable simulation of a procedure mechanism.

Although blocks nest, it is currently possible to look only one level deep with constructs like B.A , although A may be further qualified (i.e., B.A.sw or top of B.A are legal).

The following example illustrates most of the points made above about how blocks work:

1 h = .5i dh = .02i dw = .1i [ Ptr: [ boxht = h; boxwid = dw A: box B: box C: box box wid 2*boxwid "..." D: box ] Block: [ boxht = 2*dw; boxwid = 2*dw movewid = 2*dh A: box; move B: box; move C: box; move box invis "..." wid 2*boxwid; move D: box ] with .t at Ptr.s - (0,h/2) arrow from Ptr.A to Block.A.nw arrow from Ptr.B to Block.B.nw arrow from Ptr.C to Block.C.nw arrow from Ptr.D to Block.D.nw ] box dashed ht last [].ht+dw wid last [].wid+dw at last []

2 This produces

S h = .5i dh = .02i dw = .1i [ Ptr: [ boxht = h; boxwid = dw A: box B: box C: box box wid 2*boxwid "..." D: box ] Block: [ boxht = 2*dw; boxwid = 2*dw movewid = 2*dh A: box; move B: box; move C: box; move box invis "..." wid 2*boxwid; move D: box ] with .t at Ptr.s - (0,h/2) arrow from Ptr.A to Block.A.nw arrow from Ptr.B to Block.B.nw arrow from Ptr.C to Block.C.nw arrow from Ptr.D to Block.D.nw ] box dashed ht last [].ht+dw wid last [].wid+dw at last []

E

Macros

C PIC provides a rudimentary macro facility, the simple form of which is identical to that in C EQN :

1 define \f2name X \f2replacement text X

2 defines T name to be the T "replacement text" ; X is any character that does not appear in the replacement. Any subsequent occurrence of T name will be replaced by T "replacement text" .

Macros with arguments are also available. The replacement text of a macro definition may contain occurrences of $1 through $9 ; these will be replaced by the corresponding actual arguments when the macro is invoked. The invocation for a macro with arguments is

1 name(arg1, arg2, ...)

2 Non-existent arguments are replaced by null strings.

As an example, one might define a square by

1 define square X box ht $1 wid $1 $2 X

2 Then

1 square(1i, "one" "inch")

2 calls for a one inch square with the obvious label, and

1 square(0.5i)

2 calls for a square with no label:

S define square X box ht $1 wid $1 $2 X square(1i, "one" "inch") square(0.5i)

E Coordinates like T x,y may be enclosed in parentheses, as in T x,y ), ( so they can be included in a macro argument.

TROFF Interface

C PIC is usually run as a C TROFF preprocessor:

1 pic file | troff -ms

2 Run it before C EQN and C TBL if they are also present.

If the .PS line looks like

1 .PS <file

2 then the contents of file are inserted in place of the .PS line (whether or not the file contains .PS or .PE ).

Other than this file inclusion facility, C PIC copies the .PS and .PE lines from input to output intact, except that it adds two things right on the same line as the .PS :

1 .PS h w

2 h and w are the picture height and width in units. The -ms macro package has simple definitions for .PS and .PE that cause pictures to be centered and offset a bit from surrounding text.

If .PF '' `` is used instead of .PE , the position after printing is restored to where it was before the picture started, instead of being at the bottom. F '' (`` is for ``flyback.'')

Any input line that begins with a period is assumed to be a C TROFF command that makes sense at that point; it is copied to the output at that point in the document. It is asking for trouble to add spaces or in any way fiddle with the line spacing here, but point size, line thickness and font changes are generally harmless. So, for example,

1 "\eD't 20u'" circle radius .4i at 0,0 "\eD't 13u'" circle radius .2i at 0,0 "\eD't 6u'" circle radius .1i at 0,0 "\eD't 1u'" circle radius .05i at 0,0 "\eD't 3u'" .\e" don't forget to restore line thickness

2 gives

S "\D't 20u'" circle radius .4i at 0,0 "\D't 13u'" circle radius .2i at 0,0 "\D't 6u'" circle radius .1i at 0,0 "\D't 1u'" circle radius .05i at 0,0 "\D't 3u'"

E

Notice that the \eD't x' line thickness changes are in u units. They may also be given in inches provided you want a certain line width measurement.

It is also safe to muck about with sizes and fonts and local motions within quoted strings "..." ) ( in C PIC , so long as whatever changes are made are unmade before exiting the string. For example, to print text in Italics in size 6, use

1 ellipse "\es6\efISmile!\efP\es0"

2 This produces

S ellipse "\s6Smile!\s0"

E This is essentially the same rule as applies in C EQN .

There is a subtle problem with complicated equations inside C PIC pictures \(em they come out wrong if C EQN has to leave extra vertical space for the equation. If your equation involves more than subscripts and superscripts, you must add to the beginning of each equation the extra information "space 0" :

1 arrow box "$space 0 {H( omega )} over {1 - H( omega )}$" arrow

2 This produces delim $$ .EN

S arrow box "$space 0 {H( omega )} over {1 - H( omega )}$" arrow

E

C PIC generates commands for the new version of C TROFF (ditroff) that has operators for drawing graphical objects like lines, circles, and so on. As distributed, C PIC assumes that its output is going to the Varian (or Versatec) raster printers unless told otherwise with the -P option. At present, the other alternatives are -Pip (or imagen , the canon/imagen laser printer) and -Pter (a terminal or printer \(em which will do what it will with the graphical commands).

Some Examples

Herewith a handful of larger examples: .KS

S define ndblock X box wid boxwid/2 ht boxht/2 down; box same with .t at bottom of last box; box same X boxht = .2i; boxwid = .3i down; box; box; box; box ht 3*boxht "." "." "." L: box; box; box invis wid 2*boxwid "hashtab:" with .e at 1st box .w right Start: box wid .5i with .sw at 1st box.ne + (.4i,.2i) "..." N1: box wid .2i "n1"; D1: box wid .3i "d1" N3: box wid .4i "n3"; D3: box wid .3i "d3" box wid .4i "..." N2: box wid .5i "n2"; D2: box wid .2i "d2" arrow right from 2nd box ndblock spline -> right .2i from 3rd last box then to N1.sw + (0.05i,0) spline -> right .3i from 2nd last box then to D1.sw + (0.05i,0) arrow right from last box ndblock spline -> right .2i from 3rd last box to N2.sw-(0.05i,.2i) to N2.sw+(0.05i,0) spline -> right .3i from 2nd last box to D2.sw-(0.05i,.2i) to D2.sw+(0.05i,0) arrow right 2*linewid from L ndblock spline -> right .2i from 3rd last box to N3.sw + (0.05i,0) spline -> right .3i from 2nd last box to D3.sw + (0.05i,0) circlerad = .3i circle invis "ndblock" at last box.e + (.7i,.2i) arrow dotted from last circle to last box chop box invis wid 2*boxwid "ndtable:" with .e at Start.w

E .KE

The input for the picture above was:

1 .vs -2p define ndblock X box wid boxwid/2 ht boxht/2 down; box same with .t at bottom of last box; box same X boxht = .2i; boxwid = .3i; circlerad = .3i down; box; box; box; box ht 3*boxht "." "." "." L: box; box; box invis wid 2*boxwid "hashtab:" with .e at 1st box .w right Start: box wid .5i with .sw at 1st box.ne + (.4i,.2i) "..." N1: box wid .2i "n1"; D1: box wid .3i "d1" N3: box wid .4i "n3"; D3: box wid .3i "d3" box wid .4i "..." N2: box wid .5i "n2"; D2: box wid .2i "d2" arrow right from 2nd box ndblock spline -> right .2i from 3rd last box then to N1.sw + (0.05i,0) spline -> right .3i from 2nd last box then to D1.sw + (0.05i,0) arrow right from last box ndblock spline -> right .2i from 3rd last box to N2.sw-(0.05i,.2i) to N2.sw+(0.05i,0) spline -> right .3i from 2nd last box to D2.sw-(0.05i,.2i) to D2.sw+(0.05i,0) arrow right 2*linewid from L ndblock spline -> right .2i from 3rd last box to N3.sw + (0.05i,0) spline -> right .3i from 2nd last box to D3.sw + (0.05i,0) circle invis "ndblock" at last box.e + (.7i,.2i) arrow dotted from last circle to last box chop box invis wid 2*boxwid "ndtable:" with .e at Start.w

2

This is the second example: .KS

S 5 boxht = .5i; boxwid = .75i circlerad = .25i arrow "source" "code" LA: box "lexical" "analyzer" arrow "tokens" above P: box "parser" arrow "intermediate" "code" Sem: box "semantic" "checker" arrow arrow <-> up from top of LA LC: box "lexical" "corrector" arrow <-> up from top of P Syn: box "syntactic" "corrector" arrow up DMP: box "diagnostic" "message" "printer" arrow <-> right from right of DMP ST: box "symbol" "table" arrow from LC.ne to DMP.sw arrow from Sem.nw to DMP.se arrow <-> from Sem.top to ST.bot

E .KE

This is the input for the picture:

1 .PS 5 .ps 8 arrow "source" "code" LA: box "lexical" "analyzer" arrow "tokens" above P: box "parser" arrow "intermediate" "code" Sem: box "semantic" "checker" arrow \& arrow <-> up from top of LA LC: box "lexical" "corrector" arrow <-> up from top of P Syn: box "syntactic" "corrector" arrow up DMP: box "diagnostic" "message" "printer" arrow <-> right from right of DMP ST: box "symbol" "table" arrow from LC.ne to DMP.sw arrow from Sem.nw to DMP.se arrow <-> from Sem.top to ST.bot .PE

2

There are eighteen objects (boxes and arrows) in the picture, and one line of C PIC input for each; this seems like an acceptable level of verbosity.

The next example is the following: .KS

S 5i circle "DISK" arrow "character" "defns" box "CPU" "(16-bit mini)" { arrow <- from top of last box up "input " rjust } arrow CRT: " CRT" ljust line from CRT - 0,0.075 up 0.15 \ then right 0.5 \ then right 0.5 up 0.25 \ then down 0.5+0.15 \ then left 0.5 up 0.25 \ then left 0.5 Paper: CRT + 1.0+0.05,0 arrow from Paper + 0,0.75 to Paper - 0,0.5 { move to start of last arrow down 0.25 { move left 0.015; circle rad 0.05 } { move right 0.015; circle rad 0.05; " rollers" ljust } } " paper" ljust at end of last arrow right 0.25 up 0.25 line left 0.2 dotted

E

Basic Digital Typesetter .KE

This is the input for example 3:

1 .KS .PS 5i circle "DISK" arrow "character" "defns" box "CPU" "(16-bit mini)" { arrow <- from top of last box up "input " rjust } arrow CRT: " CRT" ljust line from CRT - 0,0.075 up 0.15 \e then right 0.5 \e then right 0.5 up 0.25 \e then down 0.5+0.15 \e then left 0.5 up 0.25 \e then left 0.5 Paper: CRT + 1.0+0.05,0 arrow from Paper + 0,0.75 to Paper - 0,0.5 { move to start of last arrow down 0.25 { move left 0.015; circle rad 0.05 } { move right 0.015; circle rad 0.05; " rollers" ljust } } " paper" ljust at end of last arrow right 0.25 up 0.25 line left 0.2 dotted .PE .ce Basic Digital Typesetter .sp .KE

2

Final Observations

C PIC is not a sophisticated tool. The fundamental approach \(em Cartesian coordinates and real measurements \(em is not the easiest thing in the world to work with, although it does have the merit of being in some sense sufficient. Much of the syntactic sugar (or corn syrup) \(em corners, joining things implicitly, etc. \(em is aimed at making positioning and sizing automatic, or at least relative to previous things, rather than explicit.

Nonetheless, C PIC does seem to offer some positive values. Most notably, it is integrated with the rest of the standard Unix document preparation software. In particular, it positions text correctly in relation to graphical objects; this is not true of any of the interactive graphical editors that I am aware of. It can even deal with equations in a natural manner, modulo the "space 0" nonsense alluded to above.

A standard question is, ``Wouldn't it be better if it were interactive?'' The answer seems to be both yes and no. If one has a decent input device (which I do not), interaction is certainly better for sketching out a figure. But if one has only standard terminals (at home, for instance), then a linear representation of a figure is better. Furthermore, it is possible to generate C PIC input from a program: I have used C AWK .[ [ %r 68 %R Comp. Sci. Tech. Rep. No. 68 %K CSTR %A A. V. Aho %A P. J. Weinberger %A B. W. Kernighan %T AWK - A Pattern Scanning and Processing Language %J UNIX Programmers' Manual %V 2, section 25 %D July 1978 %J Software Practice and Experience %V 9 %P 267-280 %D April 1979 .]] to extract numbers from a report and generate the C PIC commands to make histograms. This is hard to imagine with most of the interactive systems I know of.

In any case, the issue is far from settled; comments and suggestions are welcome. (Note: at Berkeley we have C GREMLIN and C GRN (for ditroff preprocessing) for interactive graphics)

Acknowledgements

I am indebted to Chris Van Wyk for ideas from several versions of C IDEAL . He and Doug McIlroy have also contributed algorithms for line and circle drawing, and made useful suggestions on the design of C PIC . Theo Pavlidis contributed the basic spline algorithm. Charles Wetherell pointed out reference [2] to me, and made several valuable criticisms on an early draft of the language and manual. The exposition in this version has been greatly improved by suggestions from Jim Blinn. I am grateful to my early users \(em Brenda Baker, Dottie Luciani, and Paul Tukey \(em for their suggestions and cheerful use of an often shaky and clumsy system.

References .[ $LIST$ .]
Appendix A: PIC Reference Manual
Pictures

The top-level object in C PIC is the ``picture'':

1 \f2picture: .PS \f2optional-width \f2element-list .PE

2 If T optional-width is present, the picture is made that many inches wide, regardless of any dimensions used internally. The height is scaled in the same proportion.

If instead the line is

1 .PS <f

2 the file f is inserted in place of the .PS line.

If .PF is used instead of .PE , the position after printing is restored to what it was upon entry.

Elements

An T element-list is a list of elements (what else?); the elements are

1 \f2element: \f2primitive attribute-list \f2placename : \f2element \f2placename : \f2position \f2variable = \f2expression \f2direction \f2troff-command { \f2element-list } [ \f2element-list ]

2

Elements in a list must be separated by newlines or semicolons; a long element may be continued by ending the line with a backslash. Comments are introduced by a # and terminated by a newline.

Variable names begin with a lower case letter; place names begin with upper case. Place and variable names retain their values from one picture to the next.

The current position and direction of motion are saved upon entry to a {...} block and restored upon exit.

Elements within a block enclosed in [...] are treated as a unit; the dimensions are determined by the extreme points of the contained objects. Names, variables, and direction of motion within a block are local to that block.

T troff-command is any line that begins with a period. Such lines are assumed to make sense in the context where they appear; accordingly, if it doesn't work, don't call.

Primitives

The primitive objects are

1 \f2primitive: box circle ellipse arc line arrow move spline "any text at all"

2 arrow is a synonym for line -> .

Attributes

An T attribute-list is a sequence of zero or more attributes; each attribute consists of a keyword, perhaps followed by a value. In the following, T e is an expression and T opt-e an optional expression.

1 \f2attribute: h(eigh)t \f2e wid(th) \f2e rad(ius) \f2e diam(eter) \f2e up \f2opt-e down \f2opt-e right \f2opt-e left \f2opt-e from \f2position to \f2position at \f2position with \f2corner by \f2e, e then dotted \f2opt-e dashed \f2opt-e chop \f2opt-e -> <- <-> same invis \f2text-list

2

Missing attributes and values are filled in from defaults. Not all attributes make sense for all primitives; irrelevant ones are silently ignored. These are the currently meaningful attributes:

1 box: height, width, at, dotted, dashed, invis, same, \f2text circle \f1and ellipse: radius, diameter, height, width, at, invis, same, \f2text arc: up, down, left, right, height, width, from, to, at, radius, invis, same, cw, <-, ->, <->, \f2text line\f1, arrow up, down, left, right, height, width, from, to, by, then, dotted, dashed, invis, same, <-, ->, <->, \f2text spline: up, down, left, right, height, width, from, to, by, then, invis, <-, ->, <->, \f2text move: up, down, left, right, to, by, same, \f2text "text...": at, \f2text

2 The attribute at implies placing the geometrical center at the specified place. For lines, splines and arcs, height and width refer to arrowhead size.

Text

Text is normally an attribute of some primitive; by default it is placed at the geometrical center of the object. Stand-alone text is also permitted. A T text-list is a list of text items; a text item is a quoted string optionally followed by a positioning request:

1 \f2text-item: "..." "..." center "..." ljust "..." rjust "..." above "..." below

2 If there are multiple text items for some primitive, they are centered vertically except as qualified. Positioning requests apply to each item independently.

Text items can contain C TROFF commands for size and font changes, local motions, etc., but make sure that these are balanced so that the entering state is restored before exiting.

Positions and places

A position is ultimately an T x,y coordinate pair, but it may be specified in other ways.

1 \f2position: \f2e, e place \(+- \f2e, e ( \f2position,\f2 position ) \f2e \f2[of the way\f2] between \f2position and \f2position \f2e < \f2position , \f2position >

2 The pair T e, e may be enclosed in parentheses.

1 \f2place: placename \f2optional-corner \f2corner placename Here \f2corner of \f2nth \f2primitive \f2nth \f2primitive \f2optional-corner

2 A T corner is one of the eight compass points or the center or the start or end of a primitive. (Not text!)

1 \f2corner: .n .e .w .s .ne .se .nw .sw .t .b .r .l .c .start .end

2

Each object in a picture has an ordinal number; T nth refers to this.

1 \f2nth: \f2nth \f2nth last

2 Since barbarisms like 1th are barbaric, synonyms like 1st and 3st are accepted as well.

Variables

The built-in variables and their default values are:

1 boxwid 0.75i boxht 0.5i circlerad 0.25i ellipsewid 0.75i ellipseht 0.5i arcrad 0.25i linewid 0.5i lineht 0.5i movewid 0.5i movewid 0.5i arrowht 0.1i arrowwid 0.05i dashwid 0.1i scale 1

2 These may be changed at any time, and the new values remain in force until changed again. Dimensions are divided by scale during output.

Expressions

Expressions in C PIC are evaluated in floating point. All numbers representing dimensions are taken to be in inches.

1 \f2expression: \f2e + \f2e \f2e - \f2e \f2e * \f2e \f2e / \f2e \f2e % \f2e \f1(modulus) - \f2e ( \f2e ) variable number \f2place .x \f2place .y \f2place .ht \f2place .wid \f2place .rad

2

Definitions

The define statement is not part of the grammar.

1 define: define \f2name X \f2replacement text X

2 delim off .EN Occurrences of $1 through $9 in the replacement text will be replaced by the corresponding arguments if name is invoked as

1 name(\f2arg1, \f2arg2, ...)

2 Non-existent arguments are replaced by null strings. T Replacement T text may contain newlines.