|
|
1.1 root 1: .so ../ADM/mac
2: .XX ideal 79 "Ideal \(em A Picture-specification Language"
3: ...\" Thu May 1 10:28:25 EDT 1986
4: ...\"refer first
5: ...TR 103
6: ...RP 80-11272-13 11173 39199-11
7: ...ND December 17, 1981
8: .hy 14 \" set hyphenation: 2=not last lines; 4= no -xx; 8=no xx-
9: .ds id \f2ideal\fP
10: .ds [. [
11: .ds .] ]
12: .nr DV 0 \" for .P1/.P2
13: .de Ks
14: .SP
15: .KS
16: ..
17: .de Ke
18: .KE
19: .SP
20: ..
21: .de IS \" do not touch these!
22: .br
23: .nf
24: ..
25: .de IE
26: ..
27: .de Ts
28: .nr PS -1
29: .nr VS -1
30: .QS
31: ..
32: .de Te
33: .nr PS +1
34: .nr VS +1
35: .QE
36: ..
37: .EQ
38: delim $$
39: .EN
40: ......graphics
41: .TL
42: Ideal \(em A Picture-specification Language
43: .br
44: User and Reference Manual\(dg
45: .AU "MH 2C-518" 5292
46: Christopher J. Van Wyk
47: .AI
48: .MH
49: .AB
50: This manual describes how to use the existing implementation of \*(id,
51: a programming language to be used for describing
52: pictures.
53: The main use of \*(id is as a preprocessor to
54: .I troff ,
55: so that pictures and text may reside in the same
56: file and be typeset together.
57: .I Ideal
58: can also produce device-independent descriptions
59: of pictures which, for example, can be displayed through
60: the
61: .UX
62: system plot filters.
63: .AE
64: .FS
65: \(dg This is a revised version of |reference(van wyk ideal cstr).
66: .FE
67: .NH 1
68: Introduction
69: .PP
70: .I Ideal
71: is a language for describing pictures.
72: It is intended primarily to operate as a
73: preprocessor to
74: .I troff |reference(troff latest reference),
75: much like
76: .I eqn |reference(latest eqn),
77: .I tbl |reference(latest tbl)
78: and
79: .I pic |reference(latest pic).
80: .PP
81: I have explained the principles that motivate
82: the form of \*(id elsewhere|reference(ideal thesis)|reference(ideal acm).
83: This document describes how to use
84: the existing implementation of \*(id
85: and treats several examples in depth.
86: .Ts
87: Paragraphs like this that appear in smaller
88: type may be skipped on first reading:
89: they present sidelights that
90: may be ignored safely by beginners.
91: .Te
92: .NH 1
93: Overview of \*(id
94: .PP
95: To take advantage of \*(id's capabilities,
96: you must believe that
97: .ce 1
98: .I "complex numbers are good" .
99: .IP \(bu
100: Complex numbers have a natural correspondence
101: to points in the Cartesian ($x-y$) coordinate
102: system.
103: .IP \(bu
104: Using complex numbers obviates the need for
105: distinguishing between ``points'' and ``dimensions.''
106: .IP \(bu
107: Complex numbers capture readily such common
108: operations as translation, rotation, and
109: reflection in the plane.
110: .LP
111: .I Ideal
112: programs define pictures by means of
113: a system of simultaneous equations in the
114: significant points of the picture and
115: a set of drawing instructions to be
116: carried out with respect to those points.
117: .I Ideal
118: solves the system of equations,
119: then draws the picture using the points
120: so determined.
121: .PP
122: All variables in \*(id programs are complex
123: numbers, with the usual operations:
124: .IP \(bu
125: component-wise addition and subtraction:
126: $(a,b) ~ +- ~ (c,d) ~ = ~ (a +- c , b +- d)$
127: .IP \(bu
128: vector multiplication:
129: $(a,b)*(c,d) ~ = ~ (ac - bd , ad + bc)$
130: .IP \(bu
131: vector division:
132: $(a,b)/(c,d) ~ = ~ (ac + bd , -ad + bc)/( c sup 2 + d sup 2 )$
133: .IP \(bu
134: component manipulation:
135: $roman "re" ((a,b)) ~ = ~ a$,
136: $roman "im" ((a,b)) ~ = ~ b$,
137: and
138: $roman "conj" ((a,b)) ~ = ~ (a,-b)$
139: .IP \(bu
140: vector magnitude calculation:
141: $roman "abs" ((a,b)) ~ = ~ sqrt { { a sup 2 } + { b sup 2 } }$
142: .IP \(bu
143: unit vector function:
144: $roman "cis" ( theta ) ~ = ~ cos theta + i sin theta$
145: .LP
146: A non-standard notation that has proved useful is
147: $alpha [ x , y ]$, for complex $x$ and $y$,
148: defined by $x + alpha ( y - x )$, and
149: meaning ``$alpha$ of the way from $x$ to $y$.''
150: .PP
151: Scalars are treated as vectors with null imaginary component.
152: For example,
153: .CW 1
154: is treated as
155: .CW (1,0) .
156: .PP
157: The scale of the coordinate system in which \*(id
158: programs are written is entirely a matter
159: of convenience.
160: The \*(id processor proper
161: produces output in the same coordinate
162: system as the input.
163: Postprocessors (``filters'') scale this coordinate
164: system to make sense for the device on which the
165: picture is displayed.
166: .PP
167: Some of the pictures below include captions keyed
168: to the associated programs.
169: Some of the labels are not produced by the program:
170: I added them to help explain the picture.
171: Such labels are parenthesized.
172: For pictures and programs that have not been
173: labeled, you may find that labeling them yourself
174: helps you understand the program.
175: .NH 1
176: Elements of the \*(id Language
177: .PP
178: This section presents statements that make up the
179: fundamental units of \*(id programs, so the
180: displayed program text represents fragments of
181: complete programs.
182: Text that appears between
183: .CW /*
184: and
185: .CW */
186: is a comment.
187: .NH 2
188: Boxes
189: .PP
190: The building blocks of \*(id programs are called
191: .I boxes ,
192: which readers familiar with programming may think
193: of as procedures or subroutines.
194: In fact,
195: the picture drawn by an \*(id program must itself
196: be a box,
197: called
198: .I main ;
199: we suppress this extra level
200: of box-nesting for all of Section 3.
201: .NH 3
202: Defining Boxes
203: .PP
204: Here is a simple box
205: and one instance of it:
206: .Ks
207: .IS
208: ...libfile rect
209: ...width 0.5
210: ...colwid 8.0
211: main {
212: put A:rect {
213: ht = 0.5;
214: wd = 1;
215: sw = 0;
216: };
217: right '(sw) ' at A.sw;
218: right '(nw) ' at A.nw;
219: left ' (se)' at A.se;
220: left ' (ne)' at A.ne;
221: }
222: .IF
223: .P1
224: rect {
225: var ne, nw, se, sw,
226: wd, ht;
227: nw = sw + (0,1)*ht;
228: ne = nw + wd;
229: se = sw + wd;
230: conn ne to nw to sw to se to ne;
231: }
232: .P2
233: .Ke
234: .LP
235: It is called
236: .I rect ,
237: has six local complex variables:
238: .I ne ,
239: .I nw ,
240: .I se ,
241: .I sw ,
242: .I wd ,
243: and
244: .I ht ,
245: three equations among these variables,
246: and an instruction to draw four lines.
247: .NH 3
248: Placing Boxes
249: .PP
250: To ask for an instance of
251: .I rect ,
252: we use a
253: .I \fIput\fP statement with a
254: .I "parameter section"
255: containing enough additional
256: equations that the local variables of
257: this instance of
258: .I rect
259: are determined uniquely.
260: For example, we might give the dimensions
261: .I ht "" (
262: and
263: .I wd )
264: and one of the corners
265: (say
266: .I sw ):
267: .Ks
268: .IS
269: ...width 0.25
270: main {
271: put rect {
272: sw = 0;
273: ht = 1;
274: wd = 0.5;
275: };
276: }
277: .IF
278: .P1
279: put rect {
280: ht = 2;
281: wd = 1;
282: sw = 0;
283: };
284: .P2
285: .Ke
286: Any of the following \fIput\fP statements would draw the same rectangle:
287: (C programmers will recognize the
288: .CW /*
289: and
290: .CW */
291: comment brackets.)
292: .P1
293: /* giving one corner, one dimension
294: and a relation on the dimensions */
295: put rect {
296: ht = 2;
297: wd = 0.5*ht;
298: nw = (0,2);
299: };
300: .P2
301: .P1
302: /* giving two adjacent corners
303: and the perpendicular dimension */
304: put rect {
305: nw = (0,2);
306: sw = 0;
307: wd = 1;
308: };
309: .P2
310: .P1
311: /* giving two diagonal corners
312: and a relation on the dimensions */
313: put rect {
314: ne = (1,2);
315: sw = 0;
316: wd = 0.5*ht;
317: };
318: .P2
319: .P1
320: /* giving three corners */
321: put rect {
322: ne = (1,2);
323: nw = (0,2);
324: se = 1;
325: };
326: .P2
327: .P1
328: /* giving the center of a side,
329: a corner, and another dimension */
330: put rect {
331: (nw+sw)/2 = (0,1);
332: nw = (0,2);
333: wd = 1;
334: };
335: .P2
336: .LP
337: The \fIput\fP statement is to \*(id what a procedure
338: call is to a conventional programming language.
339: The difference is that none of the variables
340: of a box must be specified to be a parameter
341: whose value is expected in any call:
342: any of the box's variables may be set by the
343: \fIput\fP statement, and \*(id will determine the
344: rest by means of the equations
345: in the definition of the box.
346: This means we can give whatever
347: information we know about this instance of the box,
348: as long as it is enough to determine everything uniquely.
349: This mechanism is useful because we often want to
350: set down a rectangle by giving one of its corners,
351: but not necessarily the same corner each time;
352: in a conventional programming language, we would
353: need to provide a different procedure for each
354: corner, and the code to solve for the other
355: corners from it.
356: (Almost certainly, not all of the \fIput\fP statements
357: above are equally useful; but it is good to be
358: able to use any of them when the need arises.)
359: .Ts
360: Here is how \*(id solves the system of equations
361: implicit in a program:
362: First, all equations are placed on a queue.
363: Every time a box is called, the solutions
364: to these equations may\(emand probably will\(embe
365: different, so all the equations of a box
366: are enqueued separately for each time that
367: box is put.
368: (Of course, different copies of the equation
369: refer to different copies of the variable,
370: but \*(id keeps that straight.)
371: During processing, \*(id maintains two classes
372: of variables: dependent and independent.
373: Each dependent variable is represented as a
374: linear combination of independent variables
375: plus a constant term.
376: (Variables whose values are known are a trivial
377: case of dependent variables.)
378: All variables start out independent.
379: As long as there are equations on the queue, \*(id
380: examines the head equation:
381: if, after substituting for all dependent variables,
382: the equation is linear, \*(id determines new
383: information from it if possible\(emthat is, \*(id
384: tries to make one variable dependent
385: on the others, thus reducing the number of
386: independent variables\(emor decides
387: whether it is redundant or inconsistent;
388: if the equation remains non-linear after substitution, \*(id
389: adds it to the end of the queue and proceeds.
390: If \*(id ever goes through the whole queue without
391: discovering any new information, the system cannot
392: be solved
393: (by \*(id, anyway)
394: and \*(id complains bitterly.
395: If there are any independent variables left after
396: this processing, \*(id will complain too, because
397: there is no way they can become known.
398: .Te
399: .PP
400: The variables
401: .I wd
402: and
403: .I ht
404: above are complex numbers just like the corners,
405: so we can rotate the rectangle
406: by giving them complex non-real values:
407: .Ks
408: .IS
409: ...width 0.5
410: main {
411: put rect {
412: sw = 0;
413: wd = (1,1)/abs((1,1));
414: ht = 2*wd;
415: };
416: }
417: .IF
418: .P1
419: put rect {
420: sw = 0;
421: wd = (1,1)/abs((1,1));
422: ht = 2*wd;
423: };
424: .P2
425: .Ke
426: .LP
427: (The double parentheses are needed in
428: .CW abs((1,1))
429: because
430: .CW abs(1,1)
431: is parsed as a function with two arguments.)
432: One point about this example often confuses new users:
433: the vectors
434: .I wd
435: and
436: .I ht
437: point in the
438: .I same
439: direction.
440: It is in the definition of
441: .I rect
442: that the $ht$ vector is rotated ninety degrees
443: and added to the southern points to arrive
444: at the northern points.
445: Thus, if we give a
446: .I ht
447: that is perpendicular to
448: .I wd ,
449: we get a very flat rectangle.
450: .PP
451: On the other hand, the definition of
452: .I rect
453: does not assure that
454: .I ht
455: and
456: .I wd
457: will point in the same direction.
458: This \fIput\fP statement
459: draws a parallelogram:
460: .Ks
461: .IS
462: ...width 0.25
463: main {
464: put rect {
465: ht = 1;
466: wd = (1,1)/abs((1,1));
467: sw = 0;
468: };
469: }
470: .IF
471: .P1
472: put rect {
473: ht = 1;
474: wd = (1,1)/abs((1,1));
475: sw = 0;
476: };
477: .P2
478: .Ke
479: .LP
480: Some people who feel that
481: a box called
482: .I rect
483: should draw only rectangles
484: are disturbed by this example.
485: One remedy is to add another equation
486: to the definition of
487: .I rect ,
488: asserting that two adjacent sides
489: are perpendicular; \*(id will complain if this equation
490: is not satisfied
491: (although it won't stop drawing).
492: One such equation is
493: .P1
494: wd/abs(wd) = ht/abs(ht);
495: .P2
496: .PP
497: Here is a definition for box
498: .I arrow
499: that keeps the head of the arrow symmetrical
500: about its shaft:
501: .Ks
502: .IS
503: arrow {
504: var hd, tl, head, wing;
505: head = 0.2;
506: wing = head*(tl-hd)/abs(tl-hd);
507: conn hd to tl;
508: conn hd + cis(25)*wing to hd to hd + cis(-25)*wing;
509: }
510: main {
511: put arrow {
512: hd = 0;
513: tl = (1,1);
514: };
515: }
516: .IF
517: .P1
518: arrow {
519: var hd, tl, head, wing;
520: head = 0.1;
521: wing = head*(tl-hd)/abs(tl-hd);
522: conn hd to tl;
523: conn hd + cis(25)*wing to hd to hd + cis(-25)*wing;
524: }
525: .P2
526: .Ke
527: .LP
528: Note the definition of
529: .I wing
530: in the example above:
531: the second part of the expression is a unit vector
532: that points from
533: .I tl
534: to
535: .I hd ;
536: this is multiplied by
537: .I head ,
538: the length of the ``wings'' on the arrowhead.
539: .Ts
540: When a \fIput\fP statement is interpreted, the equations in
541: its parameter section are processed before
542: the equations in its definition.
543: Thus, if there are inconsistent equations
544: between the \fIput\fP statement
545: and the box definition, the equations in
546: the \fIput\fP statement take precedence.
547: This can be useful to provide
548: default values for variables of a box.
549: For instance, if the definition of
550: .I rect
551: gave such default values to
552: .I ht
553: and
554: .I wd ,
555: they would take effect unless overridden
556: by equations in the parameter section
557: of the \fIput\fP statement.
558: .Te
559: .Ts
560: .I Ideal
561: ignores inconsistent equations, but it
562: does generate error messages about them.
563: To avoid this error message
564: about a particular equation, use a
565: tilde
566: .CW ~
567: instead of an equals
568: sign in the equation.
569: The tilde does
570: .I not
571: give the equation any lower ``priority''
572: than an equation with an equals sign:
573: all it does is shut off the error message.
574: So, the two ordered systems
575: .CW "x = 1" ,
576: .CW "x ~ 2"
577: and
578: .CW "x ~ 2" ,
579: .CW "x = 1"
580: are
581: .I different .
582: In the former, $x$ receives the value 1, and no error
583: message appears when the second equation is processed;
584: in the latter, $x$ receives the value 2, and an error
585: is generated when the second equation is encountered.
586: .Te
587: .NH 2
588: Special Boxes\(emCircles and Arcs
589: .PP
590: Boxes to draw circles and circular arcs are defined
591: in special library files.
592: .NH 3
593: Circles
594: .PP
595: The box named
596: .I circle
597: has five local variables:
598: .I center ,
599: .I radius ,
600: .I z1 ,
601: .I z2 ,
602: and
603: .I z3 .
604: The last three may be any points on the circle.
605: As above, we must give enough information to
606: determine a circle;
607: giving two points on it is insufficient, and
608: giving three collinear points is inconsistent.
609: Here are three ways to draw
610: a circle of radius one centered at the origin.
611: .Ks
612: .IS
613: ...width 0.5
614: ...libfile circle
615: dot {
616: var center;
617: center = ZAP.center;
618: put ZAP:circle {
619: radius = 0.1;
620: };
621: }
622: main {
623: put A:circle {
624: center = 0;
625: radius = 1;
626: };
627: left ' (z1)' at cis(0);
628: right '(z2) ' at cis(135);
629: right '(z3) ' at cis(245);
630: put dot {center = cis(0);};
631: put dot {center = cis(135);};
632: put dot {center = cis(245);};
633: }
634: .IF
635: .P1
636: /* giving center and radius */
637: put circle {
638: center = 0;
639: radius = 1;
640: };
641: .P2
642: .Ke
643: .P1
644: /* giving center and a point on the circle */
645: put circle {
646: center = 0;
647: z1 = 1; /* could also have given z2 or z3 */
648: };
649: .P2
650: .P1
651: /* giving three points on the circle */
652: put circle {
653: z1 = (1,0);
654: z2 = cis(135);
655: z3 = cis(235);
656: };
657: .P2
658: .LP
659: Once the circle has been determined, all five of its
660: internal variables are known.
661: So, if we ask for a circle giving three points, the
662: radius and center will be known afterward.
663: On the other hand, if we ask for a circle by giving
664: the center and the radius, the three points $z1$, $z2$,
665: and $z3$, will be known, and will be on the circle,
666: but there is no guarantee where they will be.
667: .NH 3
668: Arcs
669: .PP
670: Box
671: .I arc
672: has eight local variables:
673: .I center ,
674: .I radius ,
675: .I start ,
676: .I midway ,
677: .I end ,
678: .I startang ,
679: .I midang ,
680: and
681: .I endang .
682: It is an arc centered at point
683: .I center
684: with radius
685: .I radius ,
686: starting at point
687: .I start
688: at an angle
689: .I startang ,
690: passing through point
691: .I midway
692: at an angle
693: .I midang ,
694: and ending at point
695: .I end
696: at an angle
697: .I endang .
698: (All angles are measured with respect to $center$,
699: in degrees, with the positive $x$-axis taken to be
700: zero degrees, and the counterclockwise direction to
701: be positive.)
702: Note that $midway$ is
703: .I not
704: necessarily the midpoint of the arc!
705: If neither
706: .I midway
707: nor
708: .I midang
709: is given, the arc is drawn
710: counterclockwise from
711: .I start
712: to
713: .I end .
714: Once again, a variety of \fIput\fP statements draw the same arc:
715: .Ks
716: .IS
717: ...libfile arc
718: main {
719: put A:arc {
720: start = 1;
721: end = cis(235);
722: center = 0;
723: };
724: left ' (start)' at A.start;
725: right '(midway) ' at cis(180);
726: left ' (end)' at A.end;
727: put dot {center = A.start;};
728: put dot {center = cis(180);};
729: put dot {center = A.end;};
730: }
731: .IF
732: .P1
733: /* giving center, radius, and
734: starting and ending angles */
735: put arc {
736: center = 0;
737: radius = 1;
738: startang = 0;
739: endang = 235;
740: };
741: .P2
742: .Ke
743: .P1
744: /* giving center, starting point,
745: and ending angle */
746: put arc {
747: center = 0;
748: start = 1;
749: endang = 235;
750: };
751: .P2
752: .Ks
753: .IS
754: main {
755: put A:arc {
756: start = 1;
757: end = cis(235);
758: center = 0;
759: };
760: left ' (end)' at A.start;
761: right '(midway) ' at cis(180);
762: left ' (start)' at A.end;
763: put dot {center = A.start;};
764: put dot {center = cis(180);};
765: put dot {center = A.end;};
766: }
767: .IF
768: .P1
769: /* giving three points on the arc */
770: put arc {
771: start = cis(235);
772: midway = -1;
773: end = 0;
774: };
775: .P2
776: .Ke
777: .NH 2
778: Other Elements of \*(id Pictures
779: .PP
780: .I Ideal
781: pictures also may contain text and splines.
782: .NH 3
783: Text Captions
784: .PP
785: There are three commands to place text with respect to a point.
786: The
787: .I left
788: command left-justifies the text with respect to the specified location:
789: the text will start there.
790: The
791: .I right
792: command right-justifies the text so that it ends at the specified
793: location.
794: The default is to center the text at the point.
795: (The arrows in this picture point to the locations of the named points.)
796: .Ks
797: .P1
798: left "some text" at x;
799: .P2
800: .P1
801: \&"some centered text" at y;
802: .P2
803: .P1
804: right "some other text" at z;
805: .P2
806: .IS
807: ...colwid 6.0
808: ...width 4.0
809: arrow {
810: var tl, hd, head, perp, headang;
811: conn tl to hd;
812: perp = head*(tl-hd)/abs(tl-hd);
813: conn hd + cis(headang)*perp to hd to hd + cis(-headang)*perp;
814: head ~ 0.1;
815: headang ~ 25;
816: var start, end;
817: start = tl;
818: end = hd;
819: }
820: main {
821: var x, y, z;
822: var offset;
823: offset = (1,-1);
824: x = 0;
825: left 'some text' at x;
826: put arrow {
827: start = x + offset;
828: end = x;
829: left ' (x)' at start;
830: };
831: y = 3;
832: 'some centered text' at y;
833: put arrow {
834: start = y + offset;
835: end = y;
836: left ' (y)' at start;
837: };
838: z = 6;
839: right 'some other text' at z;
840: put arrow {
841: start = z + offset;
842: end = z;
843: left ' (z)' at start;
844: };
845: }
846: .IE
847: .Ke
848: .LP
849: To include a double quote mark in a string, escape it with a back-slash.
850: If you have a line running through a point at which you place some text,
851: you may want to add a space to one end of the text so that the line
852: doesn't chop through the text.
853: The string may include
854: .I troff
855: special characters (like
856: .CW \e(bu )
857: and commands to other preprocessors (notably
858: .I eqn ),
859: but \*(id should be run before any other preprocessors.
860: .NH 3
861: Splines
862: .PP
863: .I Ideal
864: provides quadratic splines that are drawn with a
865: B-spline basis:
866: the user supplies a sequence of guiding points, and \*(id
867: draws a smooth curve that is tangent to the polygonal
868: path they define at the midpoint of each segment;
869: the spline also starts at the first point and ends at the last.
870: .Ks
871: .IS
872: ...colwid 8.0
873: ...width 1.0
874: ...maxx 2.0
875: ...minx -1.0
876: ...maxy 2.0
877: ...miny 0.0
878: main {
879: var x, y, z, w;
880: x = 0;
881: y = 1;
882: z = (-1,1);
883: w = (2,2);
884: spline x to y to z to w;
885: right '(x) ' at x;
886: left ' (y)' at y;
887: right '(z) ' at z;
888: left ' (w)' at w;
889: put dot {center = x;};
890: put dot {center = y;};
891: put dot {center = z;};
892: put dot {center = w;};
893: }
894: .IF
895: .P1
896: spline x to y to z to w;
897:
898:
899: .P2
900: .Ke
901: .NH 1
902: Putting Boxes Together
903: .PP
904: In this section we present several complete \*(id
905: programs to show how to build up
906: pictures from boxes.
907: .NH 2
908: Naming Instances of Boxes
909: .PP
910: Most pictures involve more than placements of simple boxes.
911: And when pictures involve several boxes, certain geometrical
912: relationships should exist among them.
913: We can refer to the local variables of a box that has been put\(emall
914: we need to do is name the \fIput\fP statement.
915: Here is a simple example showing a diagram that could be used to illustrate
916: Pythagoras's Theorem for isoceles triangles:
917: .Ks
918: .IS
919: ...libfile rect
920: ...width 1.0
921: main {
922: put first: rect {
923: sw = 0;
924: ht = wd = 1;
925: };
926: put next: rect {
927: nw = first.se;
928: ht = wd = first.ht;
929: };
930: put last: rect {
931: sw = first.ne;
932: se = next.ne;
933: ht = wd;
934: };
935: }
936: .IF
937: .P1
938: main {
939: put first: rect {
940: sw = 0;
941: ht = wd = 1;
942: };
943: put next: rect {
944: nw = first.se;
945: ht = wd = first.ht;
946: };
947: put last: rect {
948: sw = first.ne;
949: se = next.ne;
950: ht = wd;
951: };
952: }
953: .P2
954: .Ke
955: .LP
956: First we place an instance of
957: .I rect
958: called
959: .I first .
960: Then we place another
961: .I rect
962: with its upper left corner ($ne$)
963: at the lower right of
964: .I first .
965: Finally, we draw a third square,
966: two of whose adjacent points are
967: identified with points on the first
968: two squares placed.
969: We could have used any two adjacent points on
970: .I last :
971: if we had placed
972: $last.ne$ at $first.ne$
973: and $last.nw$ at $next.ne$, \*(id
974: would have figured all the relationships
975: out right, although the program might be quite
976: inscrutable to humans.
977: .PP
978: How can we circumscribe a triangle?
979: If we name the \fIput\fP statement that produces
980: the instance, we can just give its three
981: vertices as values for $z1$, $z2$, and $z3$
982: in an instance of
983: .I circle .
984: .Ks
985: .IS
986: ...libfile circle
987: triangle {
988: var z1, z2, z3;
989: conn z1 to z2 to z3 to z1;
990: }
991: main {
992: put T: triangle {
993: z1 = 0;
994: z2 = 1;
995: z3 = (2,2);
996: };
997: put circle {
998: z1 = T.z1;
999: z2 = T.z2;
1000: z3 = T.z3;
1001: };
1002: }
1003: .IF
1004: .P1
1005: triangle {
1006: var z1, z2, z3;
1007: conn z1 to z2 to z3 to z1;
1008: }
1009:
1010: main {
1011: put T: triangle {
1012: z1 = 0;
1013: z2 = 1;
1014: z3 = (2,2);
1015: };
1016: put circle {
1017: z1 = T.z1;
1018: z2 = T.z2;
1019: z3 = T.z3;
1020: };
1021: }
1022: .P2
1023: .Ke
1024: .NH 2
1025: Parameter Section Commands
1026: .PP
1027: Any \*(id statement may appear in the parameter
1028: section of a \fIput\fP statement.
1029: This section illustrates some uses of this feature.
1030: .PP
1031: Suppose we will need to draw
1032: pictures of a linked list before and after
1033: insertion of a new node.
1034: We might start with this definition
1035: of a
1036: .I listnode :
1037: .Ks
1038: .IS
1039: ...width 1.0
1040: ...colwid 8.0
1041: listnode {
1042: put info: rect {
1043: var hook;
1044: hook = (nw + sw)/2;
1045: ht = lht;
1046: wd = lwd/2;
1047: };
1048: put next: rect {
1049: var c;
1050: c = (nw + se)/2;
1051: sw = info.se;
1052: ht = lht;
1053: wd = lwd/2;
1054: };
1055: }
1056: main {
1057: put listnode {
1058: info.sw = 0;
1059: var lht, lwd;
1060: lht = 1;
1061: lwd = 2;
1062: };
1063: }
1064: .IF
1065: .P1
1066: listnode {
1067: put info: rect {
1068: var hook;
1069: hook = (nw + sw)/2;
1070: ht = lht;
1071: wd = lwd/2;
1072: };
1073: put next: rect {
1074: var c;
1075: c = (nw + se)/2;
1076: sw = info.se;
1077: ht = lht;
1078: wd = lwd/2;
1079: };
1080: }
1081: .P2
1082: .Ke
1083: .LP
1084: This version of
1085: .I listnode
1086: depends on
1087: .I rect .
1088: Notice that it references two variables
1089: ($lwd$ and $lht$) that are not local to
1090: itself:
1091: these variables must be defined in any
1092: environment in which $listnode$ is put.
1093: (They are global to it.)
1094: We have added statements that define new
1095: variables ($hook$ in $first$ and $c$ in $next$)
1096: that are local to the particular instance of
1097: .I rect .
1098: .PP
1099: Now we draw the list as it is before insertion:
1100: .Ks
1101: .IS
1102: arrow {
1103: var hd, tl, head, wing;
1104: head = 0.2;
1105: wing = head*(tl-hd)/abs(tl-hd);
1106: conn hd to tl;
1107: conn hd + cis(25)*wing to hd to hd + cis(-25)*wing;
1108: }
1109: ...width 2.0
1110: main {
1111: var lht, lwd;
1112: lht = 1;
1113: lwd = 2*lht;
1114: put first: listnode {
1115: info.sw = 0;
1116: };
1117: put last: listnode {
1118: info.sw = 2[first.info.sw,first.next.se];
1119: conn next.sw to next.ne;
1120: };
1121: put new: listnode {
1122: info.nw = 2[first.next.ne,first.next.se];
1123: };
1124: put arrow {
1125: hd = last.info.hook;
1126: tl = first.next.c;
1127: };
1128: put arrow {
1129: hd = new.info.hook;
1130: tl = hd - 1;
1131: right 'new ' at tl;
1132: };
1133: put arrow {
1134: hd = first.info.hook;
1135: tl = hd - 1;
1136: right 'list ' at tl;
1137: };
1138: }
1139: .IF
1140: .P1
1141: main {
1142: var lht, lwd;
1143: lht = 1;
1144: lwd = 2*lht;
1145: put first: listnode {
1146: info.sw = 0;
1147: };
1148: put last: listnode {
1149: info.sw = 2[first.info.sw,first.next.se];
1150: conn next.sw to next.ne;
1151: };
1152: put new: listnode {
1153: info.nw = 2[first.next.ne,first.next.se];
1154: };
1155: put arrow {
1156: hd = last.info.hook;
1157: tl = first.next.c;
1158: };
1159: put arrow {
1160: hd = new.info.hook;
1161: tl = hd - 1;
1162: right "new " at tl;
1163: };
1164: put arrow {
1165: hd = first.info.hook;
1166: tl = hd - 1;
1167: right "list " at tl;
1168: };
1169: }
1170: .P2
1171: .Ke
1172: .LP
1173: Here we have added statements directly to the
1174: parameter section of two of the calls to
1175: .I arrow
1176: to avoid either naming each instance of
1177: .I arrow
1178: or naming the tails of these arrows so that
1179: text could be placed there.
1180: We have also added a statement to draw the
1181: null pointer in box
1182: .I last .
1183: .PP
1184: The program to draw the list after insertion of the
1185: new node remains largely unchanged:
1186: the nodes haven't moved; only the arrows hooking them
1187: together have moved.
1188: .Ks
1189: .IS
1190: main {
1191: var lht, lwd;
1192: lht = 1;
1193: lwd = 2*lht;
1194: put first: listnode {
1195: info.sw = 0;
1196: };
1197: put last: listnode {
1198: info.sw = 2[first.info.sw,first.next.se];
1199: conn next.sw to next.ne;
1200: };
1201: put new: listnode {
1202: info.nw = 2[first.next.ne,first.next.se];
1203: };
1204: put arrow {
1205: hd = new.info.nw;
1206: tl = first.next.c;
1207: };
1208: put arrow {
1209: hd = last.info.sw;
1210: tl = new.next.c;
1211: };
1212: put arrow {
1213: hd = new.info.hook;
1214: tl = hd - 1;
1215: right 'new ' at tl;
1216: };
1217: put arrow {
1218: hd = first.info.hook;
1219: tl = hd - 1;
1220: right 'list ' at tl;
1221: };
1222: }
1223: .IF
1224: .P1
1225: main {
1226: var lht, lwd;
1227: lht = 1;
1228: lwd = 2*lht;
1229: put first: listnode {
1230: info.sw = 0;
1231: };
1232: put last: listnode {
1233: info.sw = 2[first.info.sw,first.next.se];
1234: conn next.sw to next.ne;
1235: };
1236: put new: listnode {
1237: info.nw = 2[first.next.ne,first.next.se];
1238: };
1239: /* These two arrows are different: */
1240: put arrow {
1241: hd = new.info.nw;
1242: tl = first.next.c;
1243: };
1244: put arrow {
1245: hd = last.info.sw;
1246: tl = new.next.c;
1247: }
1248: /* These are the same as before: */
1249: put arrow {
1250: hd = new.info.hook;
1251: tl = hd - 1;
1252: right "new " at tl;
1253: };
1254: put arrow {
1255: hd = first.info.hook;
1256: tl = hd - 1;
1257: right "list " at tl;
1258: };
1259: }
1260: .P2
1261: .Ke
1262: .NH 2
1263: A Shorter Version
1264: .PP
1265: The previous example is somewhat long-winded
1266: because it demonstrates many features of \*(id
1267: that really aren't needed.
1268: For instance, it would probably have made better
1269: sense to define
1270: .I listnode
1271: as a basic element rather than building it out
1272: of
1273: .I rect s.
1274: This would reduce the need for delving deep
1275: into the internals of boxes from outside
1276: (e.g.
1277: .I first.info.hook ).
1278: Here is the same example reworked:
1279: .Ks
1280: .IS
1281: ...width 2
1282: listnode {
1283: var n, s, e, w, ne, nw, se, sw, next;
1284: n = s + (0,1)*lht;
1285: ne = n + 0.5*lwd = nw + lwd;
1286: se = s + 0.5*lwd = sw + lwd;
1287: e = (ne + se)/2;
1288: w = (nw + sw)/2;
1289: next = (ne + s)/2;
1290: conn nw to ne to se to sw to nw;
1291: conn n to s;
1292: }
1293:
1294: main {
1295: var lht, lwd;
1296: lht = 1;
1297: lwd = 2;
1298: put first: listnode {
1299: sw = 0;
1300: };
1301: put last: listnode {
1302: sw = 2[first.sw,first.se];
1303: conn s to ne;
1304: };
1305: put new: listnode {
1306: nw = 2[first.ne,first.se];
1307: };
1308: put arrow {
1309: hd = new.nw;
1310: tl = first.next;
1311: };
1312: put arrow {
1313: hd = last.sw;
1314: tl = new.next;
1315: };
1316: put arrow {
1317: hd = new.w;
1318: tl = hd - 1;
1319: right "new " at tl;
1320: };
1321: put arrow {
1322: hd = first.w;
1323: tl = hd - 1;
1324: right "list " at tl;
1325: };
1326: }
1327: .IE
1328: .P1
1329: listnode {
1330: var n, s, e, w, ne, nw, se, sw, next;
1331: n = s + (0,1)*lht;
1332: ne = n + 0.5*lwd = nw + lwd;
1333: se = s + 0.5*lwd = sw + lwd;
1334: e = (ne + se)/2;
1335: w = (nw + sw)/2;
1336: next = (ne + s)/2;
1337: conn nw to ne to se to sw to nw;
1338: conn n to s;
1339: }
1340:
1341: main {
1342: var lht, lwd;
1343: lht = 1;
1344: lwd = 2;
1345: put first: listnode {
1346: sw = 0;
1347: };
1348: put last: listnode {
1349: sw = 2[first.sw,first.se];
1350: conn s to ne;
1351: };
1352: put new: listnode {
1353: nw = 2[first.ne,first.se];
1354: };
1355: put arrow {
1356: hd = new.nw;
1357: tl = first.next;
1358: };
1359: put arrow {
1360: hd = last.sw;
1361: tl = new.next;
1362: };
1363: put arrow {
1364: hd = new.w;
1365: tl = hd - 1;
1366: right "new " at tl;
1367: };
1368: put arrow {
1369: hd = first.w;
1370: tl = hd - 1;
1371: right "list " at tl;
1372: };
1373: }
1374: .P2
1375: .Ke
1376: Notice that equations need not be just a left side and a right side:
1377: they may include several expressions that should be equal.
1378: ...........
1379: .NH 1
1380: Iteration of \*(id Constructs
1381: .NH 2
1382: Pens
1383: .PP
1384: In Donald Knuth's \s-2METAFONT\s0|reference(knuth metafont)
1385: system, pens are different
1386: shapes\(emcircles, ellipses, and polygons\(emthat draw along
1387: curves.
1388: .I Ideal
1389: includes pens as a generalization of this idea:
1390: .I any
1391: box may be used to draw along a line.
1392: For instance, users may define ``dashed'' or ``dotted'' pens.
1393: A pen statement looks like this:
1394: .SP
1395: .P1
1396: conn $x$ to $y$
1397: using $n$ $pen$ {
1398: ...
1399: } <$a$,$b$>;
1400: .P2
1401: (The only keywords in this statement are $conn$, $"to"$, and $using$.)
1402: .I Pen
1403: may be any box.
1404: .I Ideal
1405: will place $n$ copies of
1406: .I pen
1407: in the space from $x$ to $y$.
1408: $a$ and $b$ are expressions known to
1409: .I pen .
1410: The first instance of
1411: .I pen
1412: will have $a$ at $x$, and
1413: the last instance of
1414: .I pen
1415: will have $b$ at $y$;
1416: every instance in between will have
1417: its $a$ at the preceding one's $b$,
1418: and its $b$ at the succeeding one's $a$,
1419: as shown in this picture:
1420: .Ks
1421: .IS
1422: ...colwid 6.0
1423: ...width 4.0
1424: ...libfile rect
1425: pen {
1426: var e, w, c;
1427: var s;
1428: e = r.e;
1429: s = r.s;
1430: w = r.w;
1431: c = r.c;
1432: put r: rect {
1433: ht = wd = 1;
1434: };
1435: left ' \f2a\fP' at w;
1436: right '\f2b\fP ' at e;
1437: }
1438: main {
1439: put first: pen {
1440: w = 0;
1441: right '\f2x\fP ' at w;
1442: '1' at s+(0,0.1);
1443: };
1444: put second: pen {
1445: w = first.e;
1446: '2' at s+(0,0.1);
1447: };
1448: '...' at (second.e + ipth.w)/2;
1449: put ipth: pen {
1450: w = 2[second.w,second.e];
1451: '\f2i\fP\-1' at s+(0,0.1);
1452: };
1453: put ith: pen {
1454: w = ipth.e;
1455: '\f2i\fP' at s+(0,0.1);
1456: };
1457: put inth: pen {
1458: w = ith.e;
1459: '\f2i\fP+1' at s+(0,0.1);
1460: };
1461: '...' at (inth.e + last.w)/2;
1462: put last: pen {
1463: w = 2[inth.w,inth.e];
1464: '\f2n\fP' at s+(0,0.1);
1465: left ' \f2y\fP' at e;
1466: };
1467: }
1468: .IE
1469: .Ke
1470: .PP
1471: Here is an example box that contains an angular wavy path:
1472: .Ks
1473: .IS
1474: ...colwid 9.0
1475: ...width 1.0
1476: wavy {
1477: var start, end, perp, pt1, pt2, ht;
1478: perp = (0,1)*(start - end)/abs(start - end);
1479: pt1 = 0.25[start,end] + perp*ht;
1480: pt2 = 0.75[start,end] - perp*ht;
1481: conn start to pt1 to pt2 to end;
1482: }
1483: main {
1484: put wavy {
1485: start = 0;
1486: end = 1;
1487: ht = 0.2;
1488: right '(start) ' at start;
1489: left ' (end)' at end;
1490: };
1491: }
1492: .IE
1493: .P1
1494: wavy {
1495: var start, end, perp, pt1, pt2, ht;
1496: perp = (0,1)*(start - end)/abs(start - end);
1497: pt1 = 0.25[start,end] + perp*ht;
1498: pt2 = 0.75[start,end] - perp*ht;
1499: conn start to pt1 to pt2 to end;
1500: }
1501: .P2
1502: .Ke
1503: .LP
1504: Here we use
1505: .I wavy
1506: as a pen to indicate that part of a rectangle is missing.
1507: .Ks
1508: .IS
1509: main {
1510: var ne, nw, se, sw;
1511: var n1, s1, n2, s2;
1512: sw = 0;
1513: ne = nw + 2;
1514: se = sw + 2;
1515: ne = se + (0,1);
1516: n2 - 0.4 = n1 = 0.6[nw,ne];
1517: s2 - 0.4 = s1 = 0.4[sw,se];
1518: conn n1 to nw to sw to s1;
1519: conn n1 to s1
1520: using int(5*abs(n1-s1)) wavy {
1521: ht = -0.1;
1522: } <start,end>;
1523: conn n2 to ne to se to s2;
1524: conn n2 to s2
1525: using int(5*abs(n2-s2)) wavy {
1526: ht = -0.1;
1527: } <start,end>;
1528: }
1529: .IF
1530: .P1
1531: main {
1532: var ne, nw, se, sw;
1533: var n1, s1, n2, s2;
1534: ne = nw + 2;
1535: se = sw + 2;
1536: ne = se + (0,1);
1537: n2 - 0.4 = n1 = 0.6[nw,ne];
1538: s2 - 0.4 = s1 = 0.4[sw,se];
1539: conn n1 to nw to sw to s1;
1540: conn n1 to s1
1541: using int(5*abs(n1-s1)) wavy {
1542: ht = -0.1;
1543: } <start,end>;
1544: conn n2 to ne to se to s2;
1545: conn n2 to s2
1546: using int(5*abs(n2-s2)) wavy {
1547: ht = -0.1;
1548: } <start,end>;
1549: }
1550: .P2
1551: .Ke
1552: .LP
1553: We can change
1554: .I wavy
1555: to contain a smooth wave
1556: by changing the word
1557: .I conn
1558: in the fifth line of its definition to
1559: .I spline ,
1560: and use the same instructions above to draw
1561: the picture with the new pen.
1562: .Ks
1563: .IS
1564: wavy {
1565: var start, end, perp, pt1, pt2, ht;
1566: perp = (0,1)*(start - end)/abs(start - end);
1567: pt1 = 0.25[start,end] + perp*ht;
1568: pt2 = 0.75[start,end] - perp*ht;
1569: spline start to pt1 to pt2 to end;
1570: }
1571: main {
1572: var ne, nw, se, sw;
1573: var n1, s1, n2, s2;
1574: sw = 0;
1575: ne = nw + 2;
1576: se = sw + 2;
1577: ne = se + (0,1);
1578: n2 - 0.4 = n1 = 0.6[nw,ne];
1579: s2 - 0.4 = s1 = 0.4[sw,se];
1580: conn n1 to nw to sw to s1;
1581: conn n1 to s1
1582: using int(5*abs(n1-s1)) wavy {
1583: ht = -0.1;
1584: } <start,end>;
1585: conn n2 to ne to se to s2;
1586: conn n2 to s2
1587: using int(5*abs(n2-s2)) wavy {
1588: ht = -0.1;
1589: } <start,end>;
1590: }
1591: .IE
1592: .Ke
1593: .NH 2
1594: Pens as For-Statements
1595: .PP
1596: If \*(id had a for-statement, the pen statement
1597: .P1
1598: conn $x$ to $y$
1599: using $n$ $pen$ {
1600: ...
1601: } <$a$,$b$>;
1602: .P2
1603: .LP
1604: would be equivalent to
1605: .P1
1606: for $i$ = 1 to $n$ {
1607: put $pen$ {
1608: $a$ = (($i$-1)/$n$)[$x$,$y$];
1609: $b$ = ($i$/$n$)[$x$,$y$];
1610: ...
1611: };
1612: }
1613: .P2
1614: .Ts
1615: This means a pen statement can be used to synthesize
1616: a for-statement in an \*(id program.
1617: Here is a pen statement to draw a dashed arc:
1618: .Ks
1619: .IS
1620: ...libfile arc
1621: main {
1622: conn 0 to 180
1623: using 10 arc {
1624: center = 0;
1625: radius = 1;
1626: }<startang, 9+endang>;
1627: }
1628: .IF
1629: .P1
1630: conn 0 to 180
1631: using 10 arc {
1632: center = 0;
1633: radius = 1;
1634: }<startang, 9+endang>;
1635: .P2
1636: .Ke
1637: .Te
1638: .Ts
1639: To draw a set of concentric circles
1640: we can say:
1641: .Ks
1642: .IS
1643: ...libfile circle
1644: main {
1645: conn 1 to 6
1646: using 5 circle {
1647: center = 0;
1648: } <radius, radius+1>;
1649: }
1650: .IF
1651: .P1
1652: main {
1653: conn 1 to 6
1654: using 5 circle {
1655: center = 0;
1656: } <radius,radius+1>;
1657: }
1658: .P2
1659: .Ke
1660: .Te
1661: .Ts
1662: In the first example, the second expression in angle
1663: brackets is important: it means that each ``dash''
1664: covers nine degrees.
1665: In the second example, this expression is redundant:
1666: its only purpose is to prevent \*(id from generating
1667: a stream of error messages about inconsistent equations.
1668: This contorted way of simulating for-statements
1669: is necessary only because pens are meant to do
1670: the right thing at each end of and all along a path that is
1671: being drawn with some box, and not as general
1672: iteration constructs.
1673: .Te
1674: .Ts
1675: Why doesn't \*(id have
1676: for-statements?
1677: Notice that every variable in an \*(id program
1678: is assigned a value exactly once.
1679: Obviously the index of a for-statement must be
1680: an exception to that rule.
1681: Generating the index internally prevents the
1682: need to have two kinds of variables\(emchanging
1683: and fixed.
1684: The local variables of boxes placed by pen statements
1685: cannot be referenced outside the pen statement,
1686: because the boxes are not named when they are placed.
1687: A general for-statement could lead to the need to
1688: generate automatically names for different instances of boxes,
1689: a hard problem that I don't understand well enough yet.
1690: .Te
1691: ..........
1692: .NH 2
1693: Filling Regions
1694: .PP
1695: Because any box can be used as a pen, we can shade regions.
1696: (We can have not only dashed and dotted ink, but checkered paint!)
1697: Take box
1698: .I wavy ,
1699: for instance.
1700: First we construct a box
1701: .I brush ,
1702: which consists of seven copies of
1703: .I wavy
1704: going horizontally:
1705: .Ks
1706: .IS
1707: wavy {
1708: var start, end, perp, pt1, pt2, ht;
1709: perp = (0,1)*(start - end)/abs(start - end);
1710: pt1 = 0.25[start,end] + perp*ht;
1711: pt2 = 0.75[start,end] - perp*ht;
1712: conn start to pt1 to pt2 to end;
1713: }
1714: brush {
1715: var top, bot;
1716: var bwd, bht;
1717: var leftpt, rightpt;
1718: leftpt = 0.5*(top+bot) - bwd/2;
1719: rightpt = 0.5*(top+bot) +bwd/2;
1720: conn leftpt to rightpt
1721: using 7 wavy {
1722: ht = bht;
1723: }<start,end>;
1724: }
1725: main {
1726: put brush {
1727: leftpt = 0;
1728: rightpt = 7;
1729: bht = 0.3;
1730: };
1731: }
1732: .IF
1733: .P1
1734: brush {
1735: var top, bot;
1736: var bwd, bht;
1737: var leftpt, rightpt;
1738: leftpt = 0.5*(top+bot) - bwd/2;
1739: rightpt = 0.5*(top+bot) + bwd/2;
1740: conn leftpt to rightpt
1741: using 7 wavy {
1742: ht = bht;
1743: }<start,end>;
1744: }
1745: .P2
1746: .Ke
1747: Then we use ``brush'' to draw vertically
1748: over the region of interest.
1749: .Ks
1750: .IS
1751: background {
1752: conn (0,1) to (0,-1)
1753: using 6 brush {
1754: bwd = 2;
1755: bht = 0.1;
1756: }<top,bot>;
1757: }
1758: main {
1759: put background {
1760: };
1761: }
1762: .IF
1763: .P1
1764: conn (0,1) to (0,-1)
1765: using 6 brush {
1766: bwd = 2;
1767: bht = 0.1;
1768: }<top,bot>;
1769:
1770:
1771:
1772:
1773: .P2
1774: .Ke
1775: .NH 1
1776: Opaque Boxes
1777: .PP
1778: .I Ideal
1779: includes statements to blot out pieces
1780: of a picture.
1781: In this section we will sometimes place opaque boxes
1782: without explicitly drawing any background.
1783: In such cases, assume
1784: that we have painted over
1785: the area with pens as above.
1786: .NH 2
1787: Opaque Polygons
1788: .PP
1789: .I Ideal
1790: needs to know the vertices of a polygon in order
1791: to opaque the area it covers.
1792: The vertices are specified as a list in a
1793: .I boundary
1794: statement.
1795: For instance, to opaque a rectangular region using the
1796: .I rect
1797: box defined in Section 3, we could use the following statement:
1798: .Ks
1799: .IS
1800: ...libfile rect
1801: wavy {
1802: var start, end, perp, pt1, pt2, ht;
1803: perp = (0,1)*(start - end)/abs(start - end);
1804: pt1 = 0.25[start,end] + perp*ht;
1805: pt2 = 0.75[start,end] - perp*ht;
1806: conn start to pt1 to pt2 to end;
1807: }
1808: brush {
1809: var top, bot;
1810: var bwd, bht;
1811: var leftpt, rightpt;
1812: leftpt = 0.5*(top+bot) - bwd/2;
1813: rightpt = 0.5*(top+bot) +bwd/2;
1814: conn leftpt to rightpt
1815: using 7 wavy {
1816: ht = bht;
1817: }<start,end>;
1818: }
1819: background {
1820: conn (0,1) to (0,-1)
1821: using 7 brush {
1822: bwd = 2;
1823: bht = 0.1;
1824: }<top,bot>;
1825: }
1826: main {
1827: put background {
1828: };
1829: put rect {
1830: opaque;
1831: boundary = sw, se, ne, nw;
1832: sw = (-0.4,-0.4);
1833: wd = ht = 1;
1834: };
1835: }
1836: .IF
1837: .P1
1838: put rect {
1839: opaque;
1840: boundary= sw, se, ne, nw;
1841: sw = (-0.4,-0.4);
1842: wd = ht = 1;
1843: };
1844:
1845:
1846:
1847:
1848: .P2
1849: .Ke
1850: .LP
1851: The sides of the rectangle are drawn by
1852: .I rect :
1853: they are not supplied automatically by the opaquing routine.
1854: If we wanted to save only the interior of the rectangle,
1855: we could use almost the same statement:
1856: .Ks
1857: .IS
1858: ...minx -1.0
1859: ...maxx 1.0
1860: ...miny -1.0
1861: ...maxy 1.0
1862: main {
1863: put background {
1864: };
1865: put rect {
1866: opaque exterior;
1867: boundary = sw, se, ne, nw;
1868: sw = (-0.4,-0.4);
1869: wd = ht = 1;
1870: };
1871: }
1872: .IF
1873: .P1
1874: put rect {
1875: opaque exterior;
1876: boundary = sw, se, ne, nw;
1877: sw = (-0.4,-0.4);
1878: wd = ht = 1;
1879: };
1880: .P2
1881: .Ke
1882: .PP
1883: If we plan to opaque a lot of rectangles,
1884: we should include a
1885: .I boundary
1886: in the definition of
1887: .I rect .
1888: Such a default
1889: .I boundary
1890: would be referenced only if the parameter section
1891: of the \fIput\fP statement included an opaque statement
1892: .I and
1893: did not include its own
1894: .I boundary .
1895: .NH 2
1896: Opaque Circular Arc Polygons
1897: .PP
1898: The edges of opaque regions can also be circular arcs.
1899: .Ts
1900: This generalization of the simple boundary statement
1901: is the most recent addition to \*(id.
1902: It avoids treating circles and their sectors and segments
1903: as special cases, and makes opaquing circular
1904: arc polygons much easier.
1905: .Te
1906: .LP
1907: To specify a circular arc edge, one gives its endpoints
1908: and a point through which it passes;
1909: this ``pass-through'' point is marked in the boundary
1910: list by the symbol ``\f8^\fP''.
1911: For example, the boundary list for the sector shown below is
1912: .Ks
1913: .IS
1914: ...libfile arc
1915: main {
1916: put background {
1917: };
1918: put arc {
1919: center = (-1,-1);
1920: radius = 2;
1921: startang = 30;
1922: endang = 60;
1923: boundary = center,
1924: center+radius*cis(startang),
1925: ^center+radius*cis(0.5*(startang+endang)),
1926: center+radius*cis(endang);
1927: opaque;
1928: conn start to center to end;
1929: };
1930: }
1931: .IF
1932: .P1
1933: boundary = center, cis(30), ^ cis(45), cis(60);
1934:
1935:
1936:
1937:
1938:
1939: .P2
1940: .Ke
1941: .PP
1942: Another common opaque arc is the segment:
1943: .Ks
1944: .IS
1945: main {
1946: put background {
1947: };
1948: put arc {
1949: center = (-1,-1);
1950: radius = 2;
1951: startang = 0;
1952: endang = 90;
1953: boundary = start,
1954: ^center + radius*cis(0.5*(startang+endang)),
1955: end;
1956: opaque;
1957: conn start to end;
1958: };
1959: }
1960: .IF
1961: .P1
1962: boundary = cis(0), ^ cis(45), cis(90);
1963:
1964:
1965:
1966:
1967:
1968:
1969:
1970:
1971:
1972:
1973: .P2
1974: .Ke
1975: .PP
1976: One can construct an opaque circle out of two semicircular edges:
1977: .Ks
1978: .IS
1979: ...libfile circle
1980: main {
1981: put background {
1982: };
1983: put circle {
1984: radius = 0.5;
1985: center = 0;
1986: opaque;
1987: };
1988: put circle {
1989: radius = 1;
1990: center = 0;
1991: opaque exterior;
1992: };
1993: }
1994: .IF
1995: .P1
1996: boundary = cis(0), ^ cis(90), cis(180), ^ cis(270);
1997:
1998:
1999:
2000:
2001:
2002:
2003: .P2
2004: .Ke
2005: .LP
2006: Here, the outer circle has an opaque exterior,
2007: while the inner circle has an opaque interior.
2008: .NH 2
2009: Order is Important
2010: .PP
2011: Without the ability to opaque,
2012: the order in which boxes are put does not matter.
2013: But when some boxes are opaque, order obviously
2014: .I does
2015: matter.
2016: Put statements are executed
2017: in the order in which they appear in the box definition.
2018: When an opaque box is drawn, the opaquing is done
2019: .I first ,
2020: then the lines of the box are drawn;
2021: so, for instance, an opaque
2022: .I listnode
2023: does include the line down its middle separating its
2024: .I info
2025: field from its
2026: .I next
2027: field.
2028: .NH 2
2029: Some Hard Facts
2030: .PP
2031: Neither text nor splines can be used to opaque objects,
2032: nor will they be clipped properly if they are in a
2033: picture and an opaque box is placed over them.
2034: .Ts
2035: The problem with text is that \*(id operates as a
2036: .I troff
2037: preprocessor,
2038: so it cannot determine anything about the size of the
2039: text, and it needs to know that if it is to do anything
2040: involving opaquing and text.
2041: .Te
2042: .Ts
2043: The problem with splines is more subtle.
2044: When a line or circular arc is chopped,
2045: it is easy to specify the lines or circular arcs that remain.
2046: But when a spline is chopped, the guiding points of the resulting curve pieces
2047: are hard to determine.
2048: .Te
2049: .NH 1
2050: Paper Commands
2051: .PP
2052: .I Ideal
2053: includes two commands that are analogous
2054: to the way people draw on paper.
2055: .NH 2
2056: Construct
2057: .PP
2058: The
2059: .I construct
2060: statement looks just like a \fIput\fP statement,
2061: with the keyword
2062: .I put
2063: replaced by
2064: .I construct .
2065: It is best to think of it as laying a sheet of
2066: tracing paper over the current drawing.
2067: Anything constructed will be drawn on this sheet.
2068: When you return to the layer underneath,
2069: you may refer to any of the local variables
2070: (for example, with a \fIput\fP statement), but the lines and curves
2071: in the constructed picture don't show.
2072: .Ks
2073: .IS
2074: ...maxy 2.0
2075: ...minx -1.0
2076: ...maxx 1.0
2077: ...libfile rect arrow
2078: main {
2079: construct A: rect {
2080: sw = 0;
2081: wd = ht = 1;
2082: };
2083: construct B: rect {
2084: n = A.s - (0,1);
2085: wd = ht = 1;
2086: };
2087: 'top' at A.c;
2088: 'bottom' at B.c;
2089: put arrow {
2090: hd = B.n;
2091: tl = A.s;
2092: };
2093: }
2094: .IF
2095: .P1
2096: main {
2097: construct A: rect {
2098: sw = 0;
2099: wd = ht = 1;
2100: };
2101: construct B: rect {
2102: n = A.s - (0,1);
2103: wd = ht = 1;
2104: };
2105: 'top' at A.c;
2106: 'bottom' at B.c;
2107: put arrow {
2108: hd = B.n;
2109: tl = A.s;
2110: };
2111: }
2112: .P2
2113: .Ke
2114: .LP
2115: Here we have used ``invisible boxes'' to place the
2116: arrow around the text without drawing the boxes.
2117: .NH 2
2118: Draw
2119: .PP
2120: The
2121: .I construct
2122: command may also be used to localize the
2123: effects of opaque boxes.
2124: We saw above how to draw a filled polygon:
2125: paint over the area, then opaque the exterior
2126: of the polygon.
2127: But how can we draw
2128: .I two
2129: filled polygons in the same picture?
2130: .PP
2131: One solution is to
2132: construct them both, then use the
2133: .I draw
2134: command to add them to the main picture.
2135: The
2136: .I draw
2137: command transfers the contents of the
2138: named sheet of tracing paper to the sheet below.
2139: .Ks
2140: .IS
2141: ...libfile circle
2142: ...width 2.0
2143: ...colwid 10.0
2144: null {
2145: }
2146: pentagon {
2147: var center, radius,
2148: pt1, pt2, pt3, pt4, pt5;
2149: pt1 = center + radius;
2150: pt2 = center + cis(72)*radius;
2151: pt3 = center + cis(144)*radius;
2152: pt4 = center + cis(-144)*radius;
2153: pt5 = center + cis(-72)*radius;
2154: conn pt1 to pt2 to pt3 to pt4 to pt5 to pt1;
2155: boundary = pt1, pt2, pt3, pt4, pt5;
2156: }
2157: main {
2158: construct A: null {
2159: conn (0,1) to 0
2160: using 7 brush {
2161: bwd = 1;
2162: bht = 0.1;
2163: }<top,bot>;
2164: put pentagon {
2165: center = (0,0.5);
2166: radius = (0,0.5);
2167: opaque exterior;
2168: };
2169: };
2170: construct B: null {
2171: conn (0.5,0.5) to (1.5,0.5)
2172: using 5 brush {
2173: bwd = (0,1);
2174: bht = 0.1;
2175: }<top,bot>;
2176: put circle {
2177: center = (1,0.5);
2178: radius = 0.5;
2179: opaque exterior;
2180: };
2181: };
2182: draw A;
2183: draw B;
2184: }
2185: .IF
2186: .P1
2187: null {
2188: }
2189:
2190: pentagon {
2191: var center, radius,
2192: pt1, pt2, pt3, pt4, pt5;
2193: pt1 = center + radius;
2194: pt2 = center + cis(72)*radius;
2195: pt3 = center + cis(144)*radius;
2196: pt4 = center + cis(-144)*radius;
2197: pt5 = center + cis(-72)*radius;
2198: conn pt1 to pt2 to pt3 to pt4 to pt5 to pt1;
2199: boundary = pt1, pt2, pt3, pt4, pt5;
2200: }
2201:
2202: main {
2203: construct A: null {
2204: conn (0,1) to 0
2205: using 7 brush {
2206: bwd = 1;
2207: bht = 0.1;
2208: }<top,bot>;
2209: put pentagon {
2210: center = (0,0.5);
2211: radius = (0,0.5);
2212: opaque exterior;
2213: };
2214: };
2215: construct B: null {
2216: conn (0.5,0.5) to (1.5,0.5)
2217: using 5 brush {
2218: bwd = (0,1);
2219: bht = 0.1;
2220: }<top,bot>;
2221: put circle {
2222: center = (1,0.5);
2223: radius = 0.5;
2224: opaque exterior;
2225: };
2226: };
2227: draw A;
2228: draw B;
2229: }
2230: .P2
2231: .Ke
2232: .LP
2233: Even \fIput\fP statements can be added to the parameter sections
2234: of
2235: .I construct
2236: (and \fIput\fP) statements!
2237: Believe it or not, box
2238: .I null
2239: defined above is one of the most useful:
2240: it gives us a way to name a set of commands and variables
2241: so that we may reference them later, yet it doesn't
2242: require us to define a box that will be used only once.
2243: .PP
2244: Given
2245: .I construct
2246: and
2247: .I draw ,
2248: why do we need a special box
2249: .I hole
2250: to opaque a circular area without drawing anything?
2251: Why not just construct an opaque circle, then draw it
2252: on the main picture?
2253: The problem is that the effect of opaquing is localized
2254: just as much as the drawing, so an opaque constructed
2255: circle won't opaque anything that lies underneath.
2256: ........
2257: .NH 1
2258: Library Files
2259: .PP
2260: Library files are available to draw common figures and for special
2261: figures like circles and arcs.
2262: To include a library file as part of an \*(id program,
2263: include the line
2264: .P1
2265: \&...libfile $name$
2266: .P2
2267: in your \*(id program (between the
2268: .CW .IS
2269: and
2270: .CW .IE
2271: lines that mark its
2272: start and end.)
2273: This section describes available library files that have been alluded
2274: to up to now.
2275: .NH 2
2276: Rectangle
2277: .PP
2278: File
2279: .I rect
2280: contains the definition of box
2281: .I rect ,
2282: which looks like the definition in Section 3, with
2283: five more variables: $n$, $s$, $e$, $w$, and $c$,
2284: which are the four compass points and the center,
2285: respectively.
2286: .P1
2287: rect {
2288: var ne, nw, sw, se,
2289: n, e, w, s, c,
2290: ht, wd;
2291: ne = se + (0,1)*ht;
2292: nw = sw + (0,1)*ht;
2293: ne = nw + wd;
2294: n = (ne+nw)/2;
2295: s = (se+sw)/2;
2296: e = (ne+se)/2;
2297: w = (nw+sw)/2;
2298: c = (ne+sw)/2;
2299: ht ~ 1;
2300: wd ~ 1.5;
2301: boundary = ne, nw, sw, se;
2302: conn ne to nw to sw to se to ne;
2303: }
2304: .P2
2305: .NH 2
2306: Arrow
2307: .PP
2308: File
2309: .I arrow
2310: contains the definition of box
2311: .I arrow
2312: as given in Section 3.
2313: .P1
2314: arrow {
2315: var tl, hd, head, perp, headang;
2316: conn tl to hd;
2317: perp = head*(tl-hd)/abs(tl-hd);
2318: conn hd + cis(headang)*perp to hd to hd + cis(-headang)*perp;
2319: head ~ 0.1;
2320: headang ~ 25;
2321: }
2322: .P2
2323: .NH 2
2324: Wavy
2325: .PP
2326: Library file
2327: .I wavy
2328: contains the definition for the familiar
2329: .I wavy .
2330: .P1
2331: wavy {
2332: var start, end, perp, pt1, pt2, ht;
2333: perp = (0,1)*(start - end)/abs(start - end);
2334: pt1 = 0.25[start,end] + perp*ht;
2335: pt2 = 0.75[start,end] - perp*ht;
2336: conn start to pt1 to pt2 to end;
2337: }
2338: .P2
2339: .NH 2
2340: Dash
2341: .PP
2342: Library file
2343: .I dash
2344: contains the definition of box
2345: .I dash ,
2346: which may be useful for drawing dashed lines:
2347: .P1
2348: dash {
2349: var start, end;
2350: conn start to 0.25[start,end];
2351: conn 0.75[start,end] to end;
2352: }
2353: .P2
2354: ..........
2355: .NH 2
2356: Circles
2357: .PP
2358: Box
2359: .I circle
2360: resides in a library file of the same name, and
2361: has the local variables described in Section 3:
2362: $center$, $radius$, $z1$, $z2$, and $z3$.
2363: .Ts
2364: Box
2365: .I circle
2366: actually consists of variable declarations
2367: and a put of box
2368: .I CIRCLE .
2369: So, if you will have many circles of a particular radius,
2370: it might be easier for you to define your own, say, $circle3$:
2371: .P1
2372: ...libfile CIRCLE
2373: circle {
2374: var center, radius, z1, z2, z3;
2375: put CIRCLE {
2376: radius = 3;
2377: }
2378: }
2379: .P2
2380: .Te
2381: .NH 2
2382: Arcs
2383: .PP
2384: Box
2385: .I arc
2386: is contained in a library file of the same name,
2387: with local variables as described in Section 3:
2388: $center$, $radius$, $start$, $midway$, $end$,
2389: $startang$, $midang$, and $endang$.
2390: .Ts
2391: As above,
2392: .I arc
2393: calls on
2394: a box called
2395: .I ARC .
2396: .Te
2397: .NH 1
2398: Examples
2399: .NH 2
2400: B-Trees
2401: .PP
2402: This example depicts a B-tree|reference(btree) whose nodes have many children.
2403: .Ks
2404: .IS
2405: ...colwid 6.0
2406: ...width 3.0
2407: ...libfile rect
2408: arrow {
2409: var tl, hd, headvec, head;
2410: headvec = hd + head*(tl - hd)/abs(tl - hd);
2411: conn tl to hd;
2412: conn hd + cis(20)*(headvec - hd)
2413: to hd
2414: to hd + cis(-20)*(headvec - hd);
2415: }
2416:
2417: dot {
2418: var s, e;
2419: '\(bu' at 0.5[s, e] - (0,0.1);
2420: }
2421:
2422: per {
2423: var s, e;
2424: '.' at 0.5[s, e];
2425: }
2426:
2427: main {
2428: var rw, rh;
2429: rw = 2;
2430: rh = 1;
2431: var hmv, vmv;
2432: hmv = 0.6;
2433: vmv = 0.3;
2434: var ah;
2435: ah = 0.2;
2436: put root:rect{
2437: sw = (0,1);
2438: wd = rw;
2439: ht = 1;
2440: };
2441: put next:rect{
2442: sw = youngest.sw + 2*(hmv, vmv);
2443: wd = rw;
2444: ht = rh;
2445: };
2446: put youngest:rect{
2447: sw = bro.sw + (hmv, vmv);
2448: wd = rw;
2449: ht = rh;
2450: opaque;
2451: };
2452: put bro:rect{
2453: sw = eldest.sw + (hmv, vmv);
2454: wd = rw;
2455: ht = rh;
2456: opaque;
2457: };
2458: put eldest:rect{
2459: im(nw) = im(3[root.nw,root.sw]);
2460: re(0.5[nw, next.nw]) = re(0.5[root.nw, root.ne]);
2461: wd = rw;
2462: ht = rh;
2463: opaque;
2464: put arrow{
2465: head = ah;
2466: tl = 0.0[sw, se] + 0.5 * (nw - sw);
2467: hd = tl - (0, 1) * cis(-27);
2468: };
2469: put arrow{
2470: head = ah;
2471: tl = 0.33[sw, se] + 0.5 * (nw -sw);
2472: hd = tl - (0,1) * cis(-9);
2473: };
2474: put arrow{
2475: head = ah;
2476: tl = 0.67[sw, se] + 0.5 * (nw - sw);
2477: hd = tl - (0,1) * cis(9);
2478: };
2479: put arrow{
2480: head = ah;
2481: tl = 1.0[sw, se] + 0.5 * (nw -sw);
2482: hd = tl - (0,1) * cis(27);
2483: };
2484: };
2485: put arrow{
2486: head = ah;
2487: tl=root.sw + 0.5 * (root.nw - root.sw);
2488: hd=eldest.nw;
2489: };
2490: put arrow{
2491: head = ah;
2492: tl=0.1[root.sw,root.se] + 0.5 * (root.nw - root.sw);
2493: hd=bro.nw;
2494: };
2495: put a:arrow{
2496: head = ah;
2497: tl=0.2[root.sw,root.se] + 0.5 * (root.nw - root.sw);
2498: hd=youngest.nw;
2499: };
2500: put b:arrow{
2501: head = ah;
2502: tl = 0.5[root.ne, root.se];
2503: hd = next.nw;
2504: };
2505: put arrow{
2506: head = ah;
2507: hd = root.nw;
2508: tl = 0.5[root.nw, root.ne] + (0, 1.0);
2509: };
2510: conn next.nw to youngest.nw using 3 dot{}<s, e>;
2511: conn next.se to youngest.se using 3 dot{}<s, e>;
2512: conn b.tl to a.tl using 4 per{}<s, e>;
2513: }
2514: .IE
2515: .Ke
2516: .P1
2517: \&...libfile rect
2518: arrow {
2519: var tl, hd, headvec, head;
2520: headvec = hd + head*(tl - hd)/abs(tl - hd);
2521: conn tl to hd;
2522: conn hd + cis(20)*(headvec - hd)
2523: to hd
2524: to hd + cis(-20)*(headvec - hd);
2525: }
2526:
2527: dot {
2528: var s, e;
2529: '\e(bu' at 0.5[s, e] - (0,0.1);
2530: }
2531:
2532: per {
2533: var s, e;
2534: '.' at 0.5[s, e];
2535: }
2536:
2537: main {
2538: var rw, rh;
2539: rw = 2;
2540: rh = 1;
2541: var hmv, vmv;
2542: hmv = 0.6;
2543: vmv = 0.3;
2544: var ah;
2545: ah = 0.2;
2546: put root: rect {
2547: sw = (0,1);
2548: wd = rw;
2549: ht = 1;
2550: };
2551: put next: rect {
2552: sw = youngest.sw + 2*(hmv, vmv);
2553: wd = rw;
2554: ht = rh;
2555: };
2556: put youngest: rect {
2557: sw = bro.sw + (hmv, vmv);
2558: wd = rw;
2559: ht = rh;
2560: opaque;
2561: };
2562: put bro: rect {
2563: sw = eldest.sw + (hmv, vmv);
2564: wd = rw;
2565: ht = rh;
2566: opaque;
2567: };
2568: put eldest: rect {
2569: im(nw) = im(3[root.nw,root.sw]);
2570: re(0.5[nw, next.nw]) = re(0.5[root.nw, root.ne]);
2571: wd = rw;
2572: ht = rh;
2573: opaque;
2574: put arrow {
2575: head = ah;
2576: tl = 0.0[sw, se] + 0.5 * (nw - sw);
2577: hd = tl - (0, 1) * cis(-27);
2578: };
2579: put arrow {
2580: head = ah;
2581: tl = 0.33[sw, se] + 0.5 * (nw -sw);
2582: hd = tl - (0,1) * cis(-9);
2583: };
2584: put arrow {
2585: head = ah;
2586: tl = 0.67[sw, se] + 0.5 * (nw - sw);
2587: hd = tl - (0,1) * cis(9);
2588: };
2589: put arrow {
2590: head = ah;
2591: tl = 1.0[sw, se] + 0.5 * (nw -sw);
2592: hd = tl - (0,1) * cis(27);
2593: };
2594: };
2595: put arrow {
2596: head = ah;
2597: tl=root.sw + 0.5 * (root.nw - root.sw);
2598: hd=eldest.nw;
2599: };
2600: put arrow {
2601: head = ah;
2602: tl=0.1[root.sw,root.se] + 0.5 * (root.nw - root.sw);
2603: hd=bro.nw;
2604: };
2605: put a: arrow {
2606: head = ah;
2607: tl=0.2[root.sw,root.se] + 0.5 * (root.nw - root.sw);
2608: hd=youngest.nw;
2609: };
2610: put b: arrow {
2611: head = ah;
2612: tl = 0.5[root.ne, root.se];
2613: hd = next.nw;
2614: };
2615: put arrow {
2616: head = ah;
2617: hd = root.nw;
2618: tl = 0.5[root.nw, root.ne] + (0, 1.0);
2619: };
2620: conn next.nw to youngest.nw using 3 dot{}<s, e>;
2621: conn next.se to youngest.se using 3 dot{}<s, e>;
2622: conn b.tl to a.tl using 4 per{}<s, e>;
2623: }
2624: .P2
2625: .NH 2
2626: A Sector Grid
2627: .PP
2628: Norm Schryer used this figure to show how numerical integration
2629: is used to find the area of planar regions.
2630: .Ks
2631: .IS
2632: ...colwid 8.0
2633: ...width 2.0
2634: ...libfile hole
2635: gridline {
2636: var a,b;
2637: var neg, pos;
2638: conn a - neg to a + pos;
2639: }
2640:
2641: main {
2642: var n;
2643: n = 21;
2644: conn (0,0) to (0,1+1/(n-1)) using n gridline {neg = 0; pos = 1;} <a,b>;
2645: conn (0,0) to (1+1/(n-1),0) using n gridline {neg = 0; pos = (0,1);} <a,b>;
2646: put hole {
2647: radius = 1;
2648: center = (0,0);
2649: opaque exterior;
2650: };
2651: }
2652: .IF
2653: .P1
2654:
2655:
2656:
2657:
2658:
2659: \&...libfile hole
2660: gridline {
2661: var a,b;
2662: var neg, pos;
2663: conn a - neg to a + pos;
2664: }
2665:
2666: main {
2667: var n;
2668: n = 21;
2669: conn (0,0) to (0,1+1/(n-1))
2670: using n gridline {
2671: neg = 0;
2672: pos = 1;
2673: } <a,b>;
2674: conn (0,0) to (1+1/(n-1),0)
2675: using n gridline {
2676: neg = 0;
2677: pos = (0,1);
2678: } <a,b>;
2679: put hole {
2680: radius = 1;
2681: center = (0,0);
2682: opaque exterior;
2683: };
2684: }
2685: .P2
2686: .Ke
2687: .NH 2
2688: Polygon Clipping
2689: .PP
2690: This example from |reference(pavlidis image processing)
2691: illustrates all possible positions of a line
2692: segment with respect to a polygon.
2693: .Ks
2694: .ps 8
2695: .EQ
2696: delim $$
2697: gsize 8
2698: define P12 " P sub 1 ( P sub 2 ) "
2699: define P21 " P sub 2 ( P sub 1 )"
2700: .EN
2701: .IS
2702: ...width 4.5
2703: ...colwid 6.0
2704: ...libfile circle
2705: box spot{
2706: var loc;
2707: put circle{
2708: var center, radius;
2709: center = loc;
2710: radius = 0.02;
2711: opaque;
2712: };
2713: }
2714: box vert{
2715: var midd, llength;
2716: conn midd+llength to midd-llength;
2717: }
2718: box poly{
2719: var c1,p1,p2,p3,p4,p5,r1,tp,bp,ta,tb;
2720: p1 = c1+r1;
2721: p2 = cis(65)[c1,p1];
2722: p3 = cis(170)[c1,p1];
2723: p4 = cis(190)[c1,p1];
2724: p5 = cis(300)[c1,p1];
2725: tp = c1+(0,ta)*r1;
2726: bp = c1+(0,tb)*r1;
2727: conn p1 to p2;
2728: conn p2 to p3;
2729: conn p3 to p4;
2730: conn p4 to p5;
2731: conn p5 to p1;
2732: put vert{ midd=c1; llength=(0,1.6)*r1; };
2733: put spot{ loc=tp; };
2734: put spot{ loc=bp; };
2735: left '$P12$' at tp+0.1;
2736: left '$P21$' at bp+0.1;
2737: }
2738: ...minx -1
2739: ...maxx 6
2740: ...miny -1
2741: ...maxy 12
2742: box main {
2743: put poly{
2744: c1 =(1,9); r1 = (1,0); ta=1.1; tb=1.4;
2745: left '$++--$' at p2-1.5;
2746: right '(a)' at c1-(0.7,1.2);
2747: };
2748: put poly{
2749: c1 =(3.5,9); r1 = (1,0); ta=1.1; tb=0.2;
2750: left '$+---$' at p2+0.5;
2751: left '$(-+--)$' at p2+(0.5,-0.15);
2752: right '(b)(b1)' at c1-(0.7,1.2);
2753: };
2754: put poly{
2755: c1 =(1,5); r1 = (1,0); ta=1.1; tb=-1;
2756: left '$+--+$' at p2-1.5;
2757: left '$(-++-)$' at p2-(1.5,0.15);
2758: right '(c)(c1)' at c1-(0.7,1.2);
2759: };
2760: put poly{
2761: c1 =(3.5,5); r1 = (1,0); ta=0.2; tb=-1;
2762: left '$---+$' at p2+(0.5,-0.3);
2763: left '$(--+-)$' at p2+(0.5,-0.45);
2764: right '(d)(d1)' at c1-(0.7,1.2);
2765: };
2766: put poly{
2767: c1 =(1,1); r1 = (1,0); ta=-1; tb=-1.4;
2768: left '$--++$' at p4-(0,0.5);
2769: right '(e)' at c1-(0.7,1.2);
2770: };
2771: put poly{
2772: c1 =(3.5,1); r1 = (1,0); ta=0.2; tb=-0.2;
2773: left '$----$' at p1+0.2;
2774: right '(f)' at c1-(0.7,1.2);
2775: };
2776: }
2777: .IE
2778: .Ke
2779: .ps
2780: .P1
2781: \&.ps 8
2782: .EQ
2783: delim off
2784: .EN
2785: \&.EQ
2786: gsize 8
2787: define P12 " P sub 1 ( P sub 2 ) "
2788: define P21 " P sub 2 ( P sub 1 )"
2789: \&.EN
2790: \&...libfile circle
2791: \&...minx -1
2792: \&...maxx 6
2793: \&...miny -1
2794: \&...maxy 12
2795: box spot{
2796: var loc;
2797: put circle{
2798: var center, radius;
2799: center = loc;
2800: radius = 0.02;
2801: opaque;
2802: };
2803: }
2804: box vert{
2805: var midd, llength;
2806: conn midd+llength to midd-llength;
2807: }
2808: box poly{
2809: var c1,p1,p2,p3,p4,p5,r1,tp,bp,ta,tb;
2810: p1 = c1+r1;
2811: p2 = cis(65)[c1,p1];
2812: p3 = cis(170)[c1,p1];
2813: p4 = cis(190)[c1,p1];
2814: p5 = cis(300)[c1,p1];
2815: tp = c1+(0,ta)*r1;
2816: bp = c1+(0,tb)*r1;
2817: conn p1 to p2;
2818: conn p2 to p3;
2819: conn p3 to p4;
2820: conn p4 to p5;
2821: conn p5 to p1;
2822: put vert{ midd=c1; llength=(0,1.6)*r1; };
2823: put spot{ loc=tp; };
2824: put spot{ loc=bp; };
2825: left '$P12$' at tp+0.1;
2826: left '$P21$' at bp+0.1;
2827: }
2828: box main {
2829: put poly{
2830: c1 =(1,9); r1 = (1,0); ta=1.1; tb=1.4;
2831: left '$++--$' at p2-1.5;
2832: right '(a)' at c1-(0.7,1.2);
2833: };
2834: put poly{
2835: c1 =(3.5,9); r1 = (1,0); ta=1.1; tb=0.2;
2836: left '$+---$' at p2+0.5;
2837: left '$(-+--)$' at p2+(0.5,-0.15);
2838: right '(b)(b1)' at c1-(0.7,1.2);
2839: };
2840: put poly{
2841: c1 =(1,5); r1 = (1,0); ta=1.1; tb=-1;
2842: left '$+--+$' at p2-1.5;
2843: left '$(-++-)$' at p2-(1.5,0.15);
2844: right '(c)(c1)' at c1-(0.7,1.2);
2845: };
2846: put poly{
2847: c1 =(3.5,5); r1 = (1,0); ta=0.2; tb=-1;
2848: left '$---+$' at p2+(0.5,-0.3);
2849: left '$(--+-)$' at p2+(0.5,-0.45);
2850: right '(d)(d1)' at c1-(0.7,1.2);
2851: };
2852: put poly{
2853: c1 =(1,1); r1 = (1,0); ta=-1; tb=-1.4;
2854: left '$--++$' at p4-(0,0.5);
2855: right '(e)' at c1-(0.7,1.2);
2856: };
2857: put poly{
2858: c1 =(3.5,1); r1 = (1,0); ta=0.2; tb=-0.2;
2859: left '$----$' at p1+0.2;
2860: right '(f)' at c1-(0.7,1.2);
2861: };
2862: }
2863: .P2
2864: .NH 1
2865: Acknowledgements
2866: .PP
2867: My thanks to all who read drafts of this manual and
2868: criticized constructively:
2869: Al Aho,
2870: Lorinda Cherry,
2871: Eric Grosse,
2872: Steve Johnson,
2873: Brian Kernighan,
2874: John Mashey,
2875: Doug McIlroy,
2876: and
2877: Theo Pavlidis;
2878: and to early users of \*(id, especially
2879: Eric Grosse,
2880: Theo Pavlidis,
2881: Norm Schryer,
2882: and
2883: Peter Weinberger.
2884: .NH 1
2885: References
2886: .LP
2887: |reference_placement
2888: .FC
2889: .BP
2890: .2C
2891: .EQ
2892: delim $$
2893: gsize 10
2894: .EN
2895: .........
2896: .NH 1
2897: Ideal Reference Manual
2898: .PP
2899: .I Ideal
2900: programs are usually interpolated into
2901: .I troff
2902: input files.
2903: .Tm .IF S
2904: Each program must be preceded by a
2905: .CW .IS
2906: line, and followed by
2907: either a
2908: .CW .IE
2909: line or a
2910: .CW .IF
2911: line.
2912: The
2913: .CW .IE
2914: line directs that further text will
2915: follow the picture, while the
2916: .CW .IF
2917: line leaves the current
2918: position as it was when the
2919: .CW .IS
2920: line was encountered.
2921: .PP
2922: In this description, italicized names refer to nonterminal grammar symbols.
2923: A parenthesized construct followed by a star means zero or
2924: more repetitions of the construct within.
2925: Comments may appear in \*(id programs between
2926: .CW /*
2927: and
2928: .CW */
2929: brackets
2930: (which nest), and between
2931: .CW #
2932: and newline.
2933: .NH 2
2934: Elements of the \*(id Language
2935: .PP
2936: .I Ideal
2937: expects to be presented with a collection of boxes.
2938: .I Ideal
2939: prepares instructions to draw the picture specified by box
2940: .I main .
2941: If no such box exists, no picture is drawn, but the definitions
2942: that did appear are remembered.
2943: A box looks like this:
2944: .P1
2945: $identifier$ {
2946: $(statement)*$
2947: }
2948: .P2
2949: .LP
2950: The sections below describe allowed statements.
2951: An $identifier$ is a sequence of letters and digits that starts with a letter.
2952: A $string$ is a sequence of characters between double quotes.
2953: .NH 3
2954: Variable Declarations
2955: .P1
2956: var $identifier$ $( font CW "," identifier)*$ ;
2957: .P2
2958: .PP
2959: This declares complex variables for which space will be allocated whenever
2960: the box in which this statement appears is instantiated.
2961: These declarations should appear before any other statements,
2962: though this order is not enforced at the moment.
2963: .NH 3
2964: Equations
2965: .P1
2966: $expr$ = $expr$ $( ~ font CW "=" ~ expr)*$ ;
2967: $expr$ ~ $expr$ $( ~ font CW "~" ~ expr)*$ ;
2968: .P2
2969: .PP
2970: These statements declare relations that should exist among the named variables.
2971: Equations that involve more than two expressions to be equated are processed left to right.
2972: The second form (an equation with a
2973: .CW ~
2974: instead of an equals sign)
2975: requests that no error message be generated if the equation is inconsistent.
2976: Built-in operators include
2977: .CW + ,
2978: .CW -
2979: (both unary and binary),
2980: .CW * ,
2981: and
2982: .CW / ,
2983: with unary minus binding more tightly than multiplication
2984: and division, which in turn bind more tightly than addition or subtraction.
2985: Parentheses may be used to enforce a particular order of evaluation.
2986: The expression
2987: .P1
2988: ( $expr sub 1$ , $expr sub 2$ )
2989: .P2
2990: .LP
2991: has as real part the real part of $expr sub 1$ and as imaginary
2992: part the real part of $expr sub 2$.
2993: (The parentheses in this example are not metacharacters.)
2994: The expression
2995: .P1
2996: $expr sub 1$ [ $expr sub 2$ , $expr sub 3$ ]
2997: .P2
2998: .LP
2999: is evaluated as $expr sub 2 + expr sub 1 * ( expr sub 3 - expr sub 2 )$,
3000: that is
3001: ``$expr sub 1$ of the way from $expr sub 2$ to $expr sub 3$.''
3002: Built-in functions are described below.
3003: .NH 4
3004: Manipulating Complex Numbers
3005: .P1
3006: re ( $expr$ )
3007: im ( $expr$ )
3008: conj ( $expr$ )
3009: .P2
3010: .PP
3011: These functions return, respectively, the real part, the imaginary part,
3012: and the complex conjugate of $expr$.
3013: .NH 4
3014: Absolute Value
3015: .P1
3016: abs ( $expr$ )
3017: .P2
3018: .PP
3019: This returns the absolute value of $expr$.
3020: .NH 4
3021: Unit Vector Functions
3022: .P1
3023: cis ( $expr$ )
3024: E ( $expr$ )
3025: unit ( $expr$ )
3026: .P2
3027: .PP
3028: Function $font CW "cis" ()$ returns a unit vector in the direction
3029: of its argument,
3030: which is interpreted as an angle.
3031: For $theta$ in radians, $font CW "cis" ~ theta$ is defined as
3032: $e sup { i theta } ~ == ~ cos ~ theta ~ + ~ i sin ~ theta$,
3033: and
3034: $font CW "E" (x)$ is defined as $font CW "cis" ~ 2 pi x$.
3035: If $theta$ is in degrees, which it usually is in
3036: .I ideal
3037: programs (see below),
3038: .I ideal
3039: makes the necessary conversion
3040: so that $font CW "cis" ()$ and $font CW "E" ()$ work right.
3041: .PP
3042: Function $font CW "unit" ()$ returns a unit vector in the direction
3043: of its argument,
3044: which is a vector.
3045: For $z$ a complex number,
3046: $font CW "unit" (z) ~ == ~ z / font CW "abs" (z)$.
3047: .NH 4
3048: Floating Truncation
3049: .P1
3050: int ( $expr$ )
3051: .P2
3052: .PP
3053: This function returns the integer part of the real part of $expr$.
3054: .NH 4
3055: Inverse Trigonometric Function
3056: .P1
3057: angle ( $expr$ )
3058: .P2
3059: .PP
3060: This function returns the arctangent of $font CW "im" (expr)/ font CW "re" (expr)$.
3061: .NH 3
3062: Square Root Function
3063: .P1
3064: sqrt ( $expr$ )
3065: .P2
3066: .PP
3067: This function returns the square root of its complex argument.
3068: .NH 3
3069: Line Drawing
3070: .P1
3071: conn $expr$ to $expr$ $($ to $~ expr)*$ ;
3072: .P2
3073: .PP
3074: This statement directs that lines be drawn between each successive
3075: pair of points.
3076: .NH 3
3077: Box Placement
3078: .P1
3079: $[ident sub 1 font CW ":" ]$ put $ident sub 2$ { $(statement)*$ } ;
3080: put $ident sub 1$ : $ident sub 2$ { $(statement)*$ } ;
3081: .P2
3082: .PP
3083: This statement directs that box $ident sub 2$ be instantiated with the
3084: statements in braces prepended to those already present in its definition.
3085: If $ident sub 1$ is present in the statement (the optional first form,
3086: or the second form), the instantiated box's name is $ident sub 1$,
3087: and a local variable $x$ of the box may be referenced as $ident sub 1$.$x$.
3088: .NH 3
3089: Pen Drawing
3090: .P1
3091: conn $expr sub 1$ to $expr sub 2$ using $expr sub 3$
3092: $identifier$ { $(statement)*$ }
3093: < $expr sub 4$ , $expr sub 5$ > ;
3094: .P2
3095: .PP
3096: This statement is shorthand for the following loop:
3097: .P1 0
3098: for i = 1 to $expr sub 3$ by 1
3099: put $identifier$ {
3100: $expr sub 4$ = ((i-1)/$expr sub 3$)
3101: [$expr sub 1$,$expr sub 2$];
3102: $expr sub 5$ = (i/$expr sub 3$)
3103: [$expr sub 1$,$expr sub 2$];
3104: $(statement)*$
3105: };
3106: .P2
3107: .LP
3108: Note that it is different from mere text expansion because the upper limit
3109: ($expr sub 3$) may not be known explicitly when the program is written.
3110: Note too that \*(id does
3111: .I not
3112: include a for-statement!
3113: .NH 3
3114: Drawing Splines
3115: .P1
3116: spline $expr$ to $expr$ $( font CW "to" ~ expr)*$ ;
3117: .P2
3118: .PP
3119: This statement draws a spline guided by the named points in order.
3120: .NH 3
3121: Placing Captions
3122: .P1
3123: left $string$ at $expr$;
3124: $string$ at $expr$;
3125: right $string$ at $expr$;
3126: .P2
3127: .PP
3128: Use these statements to place $string$ at $expr$.
3129: The default is to center the string at $expr$.
3130: The keyword $"left"$ causes the string to start at $expr$,
3131: while the keyword $"right"$ causes the string to end there.
3132: .NH 3
3133: Constructing and Drawing Boxes
3134: .P1
3135: construct $ident sub 1$ :
3136: $ident sub 2$ { $(statement)*$ } ;
3137: $ident sub 1$ : construct
3138: $ident sub 2$ { $(statement)*$ } ;
3139: draw $identifier$ ;
3140: .P2
3141: .PP
3142: The first two of these statements are like the $put$ statements defined above,
3143: but they add to a picture named $ident sub 1$, instead of to the current picture.
3144: The third directs that the named picture (created by a $construct$ command)
3145: be added to the current picture.
3146: .NH 3
3147: Statements Related to Opaquing
3148: .P1
3149: boundary = $expr$ , $expr$ $( font CW "," expr)+$ ;
3150: opaque [ interior ] ;
3151: opaque exterior ;
3152: .P2
3153: .PP
3154: The $boundary$ statement specifies the vertices of a polygon
3155: to be opaqued.
3156: The first $opaque$ statement directs that the interior of the polygon
3157: be opaque.
3158: The second directs that the exterior be made opaque.
3159: If no boundary list is given for a circle, it will opaque a
3160: circular region.
3161: To get an opaque circular region without drawing the circle,
3162: use the box $hole$ instead.
3163: Two other boxes are available for opaquing: $sector$ opaques
3164: a sector of an arc and $segment$ opaques a segment of an arc.
3165: The variables in these boxes are the same as in $arc$.
3166: .NH 2
3167: Command-Line Options
3168: .PP
3169: These options may appear on the command line.
3170: .NH 3
3171: Selecting the Postprocessor
3172: .IP
3173: .CW -p
3174: .br
3175: .CW -4
3176: .br
3177: .CW -n
3178: .PP
3179: .I Ideal
3180: usually prepares output for processing by
3181: .I troff ;
3182: all output dimensions are expressed in inches so that
3183: .I troff
3184: can do the appropriate thing for any typesetter.
3185: The
3186: .CW -p
3187: option directs that the output be generic
3188: .I plot
3189: output.
3190: The
3191: .CW -4
3192: option causes the output to appear on the screen of a 4014.
3193: Both options cause a screen erase at each
3194: .CW .IS ,
3195: but only the
3196: .CW -4
3197: option causes a pause for input at each
3198: .CW .IE .
3199: The screen erase at
3200: .CW .IS
3201: lines may be suppressed by including the line
3202: .P1
3203: \&...noerase
3204: .P2
3205: and may be restored by the line
3206: .P1
3207: \&...yeserase
3208: .P2
3209: Option
3210: .CW -n
3211: directs that the raw
3212: .I ideal
3213: output remain uninterpreted,
3214: so it can be run through
3215: .I nroff
3216: without harm.
3217: .........
3218: .NH 3
3219: Quality Option
3220: .IP
3221: .CW -q
3222: .PP
3223: When
3224: .I ideal
3225: is used to prepare figures for typeset text,
3226: one has a choice of how to draw horizontal and vertical lines.
3227: Without the
3228: .CW -q
3229: option,
3230: .I ideal
3231: uses rule characters,
3232: which draw the lines fast, but may not be accurate.
3233: Giving the
3234: .CW -q
3235: option causes
3236: .I ideal
3237: to draw all lines,
3238: including horizontal and vertical lines, using dots;
3239: this takes longer, but is more accurate.
3240: .NH 3
3241: Angle Processing
3242: .IP
3243: .CW -r
3244: .PP
3245: .I Ideal
3246: expects angles in degrees.
3247: The
3248: .CW -r
3249: option causes
3250: .I ideal
3251: to expect them in radians instead.
3252: (See also the section on Angle Processing as a command to
3253: .I ideal ,
3254: below.)
3255: .NH 3
3256: Including Library Files
3257: .IP
3258: .CW -l\fIlibfile\fP
3259: .PP
3260: This causes
3261: .I ideal
3262: to read the named library file before it begins processing
3263: the input files or the standard input.
3264: (See also the section on Including Library Files as a command to
3265: .I ideal ,
3266: below.)
3267: .NH 2
3268: Commands to Control
3269: .I Ideal
3270: Processing
3271: .PP
3272: These commands affect decisions that must be made during the processing of
3273: an
3274: .I ideal
3275: program.
3276: They should appear after the
3277: .CW .IS
3278: delimiter,
3279: each on a separate line beginning with three dots.
3280: .NH 3
3281: Forgetting a Box
3282: .P1
3283: \&...forget $(boxname)*$
3284: .P2
3285: .PP
3286: All box definitions except the definition of $main$ survive across
3287: .CW .IS\fR/\fP.IE
3288: boundaries.
3289: If you won't need a box again, and memory space is tight, you can
3290: use the $forget$ command to reclaim the space its definition requires.
3291: Because of implementation deficiencies, at most five names of boxes may
3292: appear on one $forget$ line.
3293: There is no way to remember the $main$.
3294: .NH 3
3295: Angle Processing
3296: .P1
3297: \&...degrees
3298: .P2
3299: .P1
3300: \&...radians
3301: .P2
3302: .PP
3303: .I Ideal
3304: expects angles (arguments to $roman "cis"$
3305: and the result of $angle$)
3306: to appear in degrees,
3307: unless it was invoked with the
3308: .CW -r
3309: option on the command line,
3310: in which case it expects angles in radians.
3311: These two commands may be used to switch back and forth between
3312: degrees and radians.
3313: Only one convention may be used within a single
3314: .I ideal
3315: program,
3316: that is between
3317: .CW .IS
3318: and
3319: .CW .IE .
3320: .NH 3
3321: Including Files
3322: .P1
3323: \&...libfile $(file)*$
3324: \&...include $(file)*$
3325: .P2
3326: .PP
3327: The first command directs that the named library files be interpolated so
3328: that their box definitions may be used.
3329: The second is a way to include one's own files.
3330: The following library files are available:
3331: .I arc ,
3332: .I arrow ,
3333: .I circle ,
3334: .I dash ,
3335: .I rect ,
3336: and
3337: .I wavy.
3338: .NH 2
3339: Commands to the Postprocessor
3340: .PP
3341: Each of these commands must appear
3342: between the
3343: .CW .IS
3344: and
3345: .CW .IE
3346: delimiters
3347: on a separate line that begins with
3348: three dots.
3349: .NH 3
3350: Defining the Bounding Box
3351: .P1
3352: \&...minx $number$
3353: \&...miny $number$
3354: \&...maxx $number$
3355: \&...maxy $number$
3356: .P2
3357: .PP
3358: The
3359: .I ideal
3360: processor computes the minimum and maximum extent of the
3361: picture in both $x$- and $y$-directions so that the bounding box
3362: (hence, the picture) can be scaled to the desired width.
3363: These commands allow you to override
3364: .I ideal 's
3365: computed bounding box.
3366: They are useful when the computed bounding box is too large or too small.
3367: .I Ideal
3368: cannot compute the bounding box of a string, so it doesn't try,
3369: and this may be too liberal.
3370: .PP
3371: Normally these dimensions are computed anew for each picture produced
3372: by
3373: .I ideal .
3374: Use the line
3375: .P1
3376: \&...obbox
3377: .P2
3378: between subsequent
3379: .CW .IS
3380: and
3381: .CW .IE\fR/\fP.IF
3382: lines to keep the
3383: bounding box of the previous picture.
3384: .NH 3
3385: Setting the Picture Width
3386: .P1
3387: \&...width $number$
3388: .P2
3389: .PP
3390: This command causes the bounding box to be scaled to a width of $number$
3391: inches.
3392: (If not specified, the default is four inches.)
3393: It persists across
3394: .CW .IS\fR/\fP.IE
3395: boundaries.
3396: .NH 3
3397: Setting the Picture Height
3398: .P1
3399: \&...height $number$
3400: .P2
3401: .PP
3402: This command causes the bounding box to be scaled to a height of $number$
3403: inches.
3404: This allows one to impose a different scale on the horizontal and vertical
3405: dimensions of a picture.
3406: The setting persists across
3407: .CW .IS\fR/\fP.IE
3408: boundaries.
3409: ........
3410: .NH 3
3411: Setting the Column Width
3412: .P1
3413: \&...colwid $number$
3414: .P2
3415: .PP
3416: This command gives the width of the column in which the picture will appear
3417: so that it can be centered in the column.
3418: (If not specified, the default is six inches.)
3419: The setting persists across
3420: .CW .IS\fR/\fP.IE
3421: boundaries.
3422: .NH 3
3423: Using Postprocessor Commands to Scale Pictures
3424: .PP
3425: The bounding box and width information can be adjusted
3426: to scale the picture or move it on the page.
3427: .I Ideal
3428: does not check that the entire picture is in fact
3429: contained in the bounding box, and it cannot check
3430: that the column width is correct:
3431: it uses the bounding box information to scale the
3432: picture to the width requested,
3433: then uses the column width to center the bounding box.
3434: .PP
3435: For example, to produce figures at the right of the
3436: page,
3437: .I ideal
3438: was told that the column was nine inches wide.
3439: Since the pictures are typically no more than two inches
3440: wide, they lie within the actual six inch column.
3441: .PP
3442: Suppose one's
3443: figures are drawn in the unit square,
3444: though some pieces lie a little outside,
3445: and one wants one inch to correspond to one in \*(id's
3446: coordinate system.
3447: One could set the six parameters as follows:
3448: .P1
3449: \&...minx 0
3450: \&...miny 0
3451: \&...maxx 1
3452: \&...maxy 1
3453: \&...width 1
3454: \&...colwid 6
3455: .P2
3456: .LP
3457: This gives a way for
3458: users to get exact scale
3459: representations of their pictures.
3460: .NH 2
3461: Obsolete Features
3462: .PP
3463: These holdovers from earlier implementations are not guaranteed to survive.
3464: .NH 3
3465: Options on the
3466: .CW .IS
3467: Line
3468: .PP
3469: One could specify the bounding box on the
3470: .CW .IS
3471: line by
3472: .P1
3473: \&.IS minx maxy maxx miny
3474: .P2
3475: .LP
3476: The bounding box commands to the postprocessor are now preferred.
3477: .NH 3
3478: Changes to Keywords
3479: .PP
3480: The keyword $box$ used to be required before a box definition.
3481: .PP
3482: The keyword $boundary$ has replaced $bdlist$.
3483: .PP
3484: The function $angle$ used to be called $atan2$.
3485: .NH 3
3486: String Delimiters
3487: .PP
3488: Strings used to be delimited by single quotes.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.