|
|
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.