wireframe Line diagrams with troff pic

Create professionally formatted line diagrams using troff pic

When you think of scientific writing, you may immediately consider using LaTeX. After all, many scientific and engineering journals accept manuscripts in LaTeX format, including AMS and ACM. But before Don Knuth created the TeX system in 1978, and long before Leslie Lamport released the LaTeX macros in 1984, technical writers at AT&T Labs used the troff system on Unix to produce professionally formatted scientific documents.

The Unix troff system formatted documents for printing using a C/A/T graphics phototypesetter. This was updated in 1980 to ditroff (device independent troff) to add support for a CRT-based graphic phototypesetter, the Mergenthaler Linotron 202. Where the C/A/T only supported text, using fonts stored on film, the 202 could generate other graphical output such as arbitrary line diagrams.

Line diagrams are important in scientific documents; they can illustrate an experiment, or provide a diagram that explains a set of equations. Brian Kernighan described a system to produce line diagrams in a 1984 AT&T Bell Labs Computing Science Technical Report (No. 116). This was the pic pre-processor, which provided output suitable for ditroff to produce beautifully and professionally formatted scientific documents.

Let's examine a real-world scientific document that uses line diagrams with the pic pre-processor. In these examples, I'll use the implementation provided by GNU groff, which is available for all modern operating systems.

Starting a new document

For a hobby project, I needed to draw an equilateral triangle in 640×480 graphics. I knew the top vertex of the triangle should be centered at the top of the screen. What coordinates should I use to draw the other two vertices? I started by drawing a diagram, which I'll show here.

Let's say I wanted to write a paper about this process, using troff. Formatting the document with the ms macros developed at AT&T Bell Labs, I might start a document like this:

.ds FAM N
.nr PS 14
.TL
Drawing an equilateral triangle
.LP
Start with this representation of an equilateral triangle in a 640\[mu]480
display. (That's a 4:3 aspect ratio.) The triangle in the diagram doesn't
need to be exactly to scale, it can be approximate.

This is a typical troff document with the ms macros. The .TL macro defines the document's title; I have omitted the .AU (author) and .AI (author institution) macros for simplicity. In most examples of technical writing, the first paragraph after a title or heading should be left-aligned (also called "block") so this paragraph uses the .LP (left paragraph) macro. Following paragraphs would use .PP (first-line indented paragraphs) but this sample document only has one paragraph. The \[mu] text is a troff escape sequence to insert a multiplication sign. You can find a full reference for all character sequences in the groff_char(7) man page.

At the very top of the document, before using any other document macros, I've set two variables: The FAM variable is the font family that ms will use to format the document; I've used N to use New Century Schoolbook for readability. The PS number register is the document's default point size, which I've set to 14 points so the output will be easier to see without zooming. The groff_ms(7) man page lists all of the formatting macros provided in the ms package.

Drawing basic shapes

To add a line diagram using the pic pre-processor, add the .PS (picture start) and .PE (picture end) macros, with the pic instructions between them. The pic program will interpret only the instructions between .PS and .PE, leaving other lines for troff to format.

The line drawing commands are more fully described in the GNU groff documentation, especially in the Making Pictures with GNU PIC document by Eric S. Raymond (and included with GNU groff), but I'll briefly describe them here:

The pic system supports several base shapes: box, circle, ellipse, line, arrow, and arc. The closed shapes can use height and width descriptors; lines and arrows typically use directional vectors like down or right with a distance. For example, to draw a box that is 4 inches wide and 3 inches high, we could write:

.PS
box width 4 height 3;
.PE
a rectangle, outlined in gray

The width and height descriptors can also be abbreviated:

.PS
box wid 4 ht 3;
.PE

Shapes are drawn with solid lines by default unless you provide a modifier to draw dashed lines (dashed) or dotted lines (dotted).

.PS
box dotted wid 4 ht 3;
.PE
a rectangle, with a dotted outline

Each instruction ends with a semicolon, so pic knows where one shape description ends and the next begins. The next shape will be drawn at the "end" of the previous shape, unless you provide an origin, such as from last box to mean the end of a previous box. You can specify a position relative to a box or other closed shape with the cardinal directions .n ("North"), .e ("East"), .s ("South"), and .w ("West") to mean the top, right, bottom, and left edges of a closed shape. You can also use mid-point values with .nw, .ne, .se, and .sw for the top-left ("North West"), top-right ("North East"), bottom-right ("South East"), and bottom-left (South West) coordinates on the shape.

Let's put that together by drawing a dashed line that equally divides a dotted box:

.PS
box dotted wid 4 ht 3;
line dashed from last box.n down 3;
.PE
a rectangle, with a dotted outline and a line down the middle

Drawing within the shape

To draw the equilateral triangle inside the box, we use a series of three line shapes. This also provides an opportunity to label each line by adding text between double quotes.

Let's practice that by updating the complete document, with a line diagram showing three lines to create an approximation of an equilateral triangle: the hypotenuse is c and each half of the base is a. At the same time, we can label the vertical line as b to define a right triangle in the interior of the equilateral triangle.

I've replaced line with arrow so you can more clearly see each line segment:

.ds FAM N
.nr PS 14
.TL
Drawing an equilateral triangle
.LP
Start with this representation of an equilateral triangle in a 640\[mu]480
display. (That's a 4:3 aspect ratio.) The triangle in the diagram doesn't
need to be exactly to scale, it can be approximate.
.PS
box dotted wid 4 ht 3;
arrow "c" from last box.n down 3 left 1.5;
arrow "a" right 1.5;
arrow "a" right 1.5;
arrow "c" up 3 left 1.5;
arrow "b" dashed down 3;
.PE

On original Unix systems in the 1970s and 1980s, authors would process a document like this via the command line, running the pic pre-processor and "piping" the output to the troff program. The output would be redirected to a file so it could be printed on a typesetter:

$ pic triangle.ms | troff -ms > output

However, the modern GNU groff system instead uses the pdfroff command to generate a PDF document; this command can also use the same -ms option to use the ms macros. Add the -p option to use the pic pre-processor:

$ pdfroff -ms -p triangle.ms > triangle.pdf
rectangle with a triangle drawn inside it, the triangle is labeled a, b, c
The formatted output, as a PDF

Positioning the labels

This looks almost correct. Note that the labels are printed on top of each of the lines. This makes the text difficult to read. We can improve the layout by adding an above or below specifier to the label, to position the text above or below the line. We can't use right or left to position the text, because those words are reserved for defining the line (such as right 1 to draw a line that goes 1 inch to the right). Instead, we can justify the text to the left or right using the ljust and rjust specifiers.

Justifying the text makes the labels look better, but I find the text can look a little "crowded" next to the line. That's why I usually add an extra space in the label to "push" the label further off to one side:

.ds FAM N
.nr PS 14
.TL
Drawing an equilateral triangle
.LP
Start with this representation of an equilateral triangle in a 640\[mu]480
display. (That's a 4:3 aspect ratio.) The triangle in the diagram doesn't
need to be exactly to scale, it can be approximate.
.PS
box dotted wid 4 ht 3;
line "c " rjust from last box.n down 3 left 1.5;
line "a" below right 1.5;
line "a" below right 1.5;
line " c" ljust up 3 left 1.5;
line "b " rjust dashed down 3;
.PE

The result is a line diagram that is properly labeled and easy to read:

$ pdfroff -ms -p triangle.ms > triangle.pdf
rectangle with a triangle drawn inside it, the triangle is labeled a, b, c
The formatted output, as a PDF

Line diagrams with pic

The pic pre-processor is a fairly simple way to insert diagrams in a scientific document. I find the pic drawing instructions to be quite natural and easy to remember. Sometimes, I might need to think about how to describe a diagram to pic in a way that shapes will naturally follow one another.

For a more detailed walkthrough of how to create line diagrams in pic, I recommend the Making Pictures with GNU PIC document by Eric S. Raymond, which is included with GNU groff. On Linux systems, you can find this document in the /usr/share/doc/groff directory.