1# Differences Between Pikchr And Legacy-PIC 2 3Pikchr is mostly compatible with legacy PIC in the sense that it will 4run most of the example scripts contained in the 5[original technical report on PIC by BWK][bwk] with little to no change. 6Nevertheless, some features of legacy PIC have been omitted, and new 7features have been added. This article attempts to highlight the 8important differences. 9 10[bwk]: /uv/pic.pdf 11 12Pikchr is implemented from scratch, without reference to the original 13PIC code, and without even access to a working version of legacy PIC 14with which to perform experiments. The syntax implemented by Pikchr 15is based solely on the descriptions in the [BWK tech report][bwk] which was 16intended as a user manual, not a precise description of the language. 17Consequently, some details of Pikchr may differ from PIC without our 18even being aware of it. This document tries to list the differences 19that we know of. But there are likely omissions. 20 21## Designed for the Web 22 23Pikchr is designed to be embedded in Markdown and generate SVG 24output which blends with the HTML generated by Markdown. It is 25intended for use in software development and software project 26management systems for the 2020s and beyond. 27 28PIC was designed to be embedded in [troff][troff] - an historically 29significant but now obsolete markup language developed at Bell Labs 30in the late 1970s and early 1980s. 31PIC could include troff markup in the middle of 32a drawing, a capability omitted from Pikchr (obviously). 33 34[troff]: https://en.wikipedia.org/wiki/Troff 35 36## New Object Types 37 38Pikchr supports several new object types that were unavailable 39in PIC. 40 41~~~ pikchr indent 42oval "oval" 43move 44cylinder "cylinder" 45move 46file "file" 47move 48dot " dot" ljust 49~~~ 50 51Additional object types may be added in subsequent versions of Pikchr. 52 53## Units Other Than Inches 54 55PIC operated purely in inches. Pikchr allows you to attach a 56units designator on numeric literals so that distances can be easily 57expressed in other units. For example, you can write "`2.3cm`" to 58mean 2.3 centimeters. This is easier and more intuitive than writing 59something like 60"`2.3/2.54`". Pikchr still does all of its calculations in inches, 61internally. The "cm" suffix is actually part of the numeric literal 62so that "`2.3cm`" is really just an alternative spelling for "`0.905`". 63 64Units supported by Pikchr include: 65 66 * `cm` → centimeters 67 * `in` → inches (the default) 68 * `mm` → millimeters 69 * `pc` → picas 70 * `pt` → points 71 * `px` → pixels 72 73Because the units are part of the numeric literal, 74the unit designator cannot be separated from the number by whitespace. 75Units only apply to numeric literals, not to expressions. 76 77 78## New Uses For "`radius`": 79 80A positive "`radius`" attribute on "`box`" items causes the box 81to be displayed with rounded corners: 82 83~~~ pikchr indent 84box rad 15px "box" "radius 15px" 85~~~ 86 87Similarly a "`radius`" value on a "`line`" or "`arrow`" with 88multiple segments rounds the corners: 89 90~~~ pikchr indent 91arrow rad 10px go heading 30 then go 200% heading 175 \ 92 then go 150% west "arrow" below "radius 10px" below 93~~~ 94 95## The "`color`" and "`fill`" attributes 96 97Any object can have a "`color`" attribute to set its foreground 98color and a "`fill`" attribute to set its background color. The 99default "`color`" is black and the default "`fill`" is "None". 100 101~~~ pikchr indent 102boxrad = 12px 103box color blue "color blue" 104move 105box fill lightgray "fill lightgray" 106move 107box color white fill blue "color white" "fill blue" 108~~~ 109 110## The "`thickness`" attribute 111 112The new "`thickness`" attribute specifies the stroke-width. You can 113also use attributes "`thick`" and "`thin`" to increase or decrease the 114stroke-width in increments. 115 116~~~ pikchr indent 117boxrad = 12px 118box thin "thin" 119move 120box "(default)" italic 121move 122box thick "thick" 123move 124box thick thick "thick" "thick" 125~~~ 126 127## Enhanced ability to control text alignment and display 128 129There are new modifiers for text labels: 130 131~~~ pikchr indent 132box "bold" bold "italic" italic "big" big "small" small fit 133line from 1cm right of previous.se to 3cm right of previous.ne \ 134 "aligned" above aligned 135~~~ 136 137## Adjust the size of objects to fit their text annotations 138 139The ["`fit`" attribute](./fit.md) adjusts the width and height of 140box-like objects to snugly surround their text labels. 141 142Also, if the width or height of an object is zero after all attributes 143have been parsed, then the zero dimensions are increased to enclose 144the text annotations. 145 146## Change numeric property values by a percentage 147 148You can change the value of a numeric attribute by a percentage, 149rather than having to specify a particular value: 150 151~~~ pikchr indent 152box "default" italic "box" italic 153move 154box "width 150%" width 150% 155move 156box "wid 75%" wid 75% 157~~~ 158 159## The "`chop`" attribute works differently 160 161The "`chop`" attribute is completely redesigned. It takes no 162argument and can only appear once. If "`chop`" is specified on 163a line (or arrow or spline) then end-points of the line that 164would have landed on the center of a box-like object (box, 165circle, cylinder, ellipse, file, or oval) are shortened to 166land exactly on the border of that object. 167 168~~~ pikchr indent 169file "A" 170cylinder "B" at 5cm heading 125 from A 171arrow <-> from A to B chop "from A to B chop" aligned above 172~~~ 173 174## The "`same as` *object*" construct 175 176An ordinary "`same`" attribute works as in PIC - it copies the 177configuration of the previous object of the same class. Pikchr 178is extended with the "`same as` *object*" clause, that copies the 179configuration from any other prior object, including objects of 180different types. 181 182~~~ pikchr indent 183box thick thick fill lightgray "box" "thick" "fill lightgray" 184move 185file same as last box "file" "same as" "last box" rad filerad 186~~~ 187 188## New ways to describe line paths 189 190 * **go** *distance* **heading** *compass-angle* 191 * **go** *distance* *compass-point* 192 * **go** *direction* **until even with** *place* 193 * **close** 194 195## New syntax to describe positions 196 197 * *distance* **above**|**below** *position* 198 * *distance* **left**|**right** **of** *position* 199 * *distance* **heading** *compass-angle* **from** *position* 200 * *nth* **vertex of** *line-object* 201 202 203## New ways to identify prior objects 204 205Pikchr allows the keywords "`last`" or "`previous`" to refer to 206the immediately previous object without having to specify the 207type of that object. 208 209Objects that contain text that looks like a label (starts with 210an upper-case letter and contains only letters, digits, and underscores) 211can be used as a label for that object. Thus if you say: 212 213~~~ 214 N1: circle "Node1" 215~~~ 216 217Subsequent code can refer to that circle as either "`N1`" or as "`Node1`". 218 219## Support for C and C++ style comments 220 221Pikchr continues to support Bourne shell style “`#`” comments: 222a `#` character and all following 223characters until end-of-line. 224 225As an extension to PIC, Pikchr also recognizes 226C and C++ style comments: “`//`” to end of line and block comments 227beginning with “`/*`”, extending through “`*/`”, irrespective of 228any intervening newlines. 229 230*Example:* 231 232 box "Hello," # say “hi” 233 box "world!" // complete the thought 234 box "Hello," "world!!" /* You may also break the 235 lines, like this. */ 236 237## Variable names can start with "`$`" or "`@`" characters 238 239There are many built-in variable names and keywords in the PIC and 240Pikchr languages, all of which currently begin with lowercase letters. To 241reduce the chance of a collision between an application-defined 242variable and a built-in variable name or keyword, Pikchr allows 243application-defined variable names to begin with "`$`" or "`@`". 244Pikchr does not now — nor will it ever — pre-define variables that 245begin with "`$`" or "`@`", other than the use of positional macro 246parameters `$1`, `$2`, etc. 247 248We recommend that you begin your own variable names with either 249"`$`" or "`@`" to ensure that they will never collide with variables 250that might be added to future version of Pikchr. 251 252## New assignment operators for variables 253 254Both Pikchr and PIC allow statements that assign values to 255built-in or user-defined variables, like this: 256 257> *variable* **=** *expr* 258 259Pikchr adds several new assignment operators: 260 261 * += 262 * -= 263 * *= 264 * /= 265 266The new operators are handy for scaling the value of an existing 267variable. For example, to make the default radius of circles 26825% smaller: 269 270~~~~ 271 circlerad *= 0.75 272~~~~ 273 274## New keyword aliases 275 276Pikchr allows certain aliases for keywords that are not 277recognized by PIC: 278 279 * "`invisible`" ⇆ "`invis`" 280 * "`first`" ⇆ "`1st`" 281 * "`previous`" ⇆ "`last`" 282 283## The "`text`" Object 284 285With PIC, you create new text items by placing a string 286literal as the first token in a statement. Pikchr works the 287same way, and further allows you to use the class name "`text`" 288as the first token of the statement. 289 290## New variables 291 292 * bottommargin 293 * charht 294 * charwid 295 * color 296 * fill 297 * fontscale 298 * leftmargin 299 * margin 300 * rightmargin 301 * thickness 302 * topmargin 303 304If the "fontscale" variable exists and is not 1.0, then the point-size 305of fonts is increased or decreased by multiplying by the fontscale. 306This variable can be used to increase or decrease the fonts in a 307diagram relative to all the other elements. 308 309The "charht" and "charwid" variables should contain an estimate for 310the average height and width of a character. This information is used 311when trying to estimate the size of text. Because Pikchr has no access 312to the rendering engine, it cannot precisely determine the bounding box 313for text strings. It tries to make a guess, and takes into account that 314some letters (like "w") are wider than others (like "i"). But Pikchr 315can only guess at the actual size of text strings. Usually this guess 316is close enough. Some scripts might need to compensate, however, by 317adding leading or trailing spaces to the text strings, or by adjusting 318the values for "charht" and "charwid". 319 320Setting the "`margin`" variable to a distance adds that amount of 321extra whitespace around all four sides of the diagram. The other 322four margin variables ("rightmargin", "bottommargin", "leftmargin", 323and "topmargin") add extra whitespace to that one side. The two 324methods are additive. For example, to add one centimeter of extra 325space on all sides except the left, you could write: 326 327~~~ 328 margin = 1cm; 329 leftmargin = -1cm; 330~~~ 331 332The "thickness", "color", and "fill" variables determine the default 333value for the "thickness", "color", and "fill" attributes on all objects. 334Because the attribute name and the variable name are the same, the 335variable name can only be accessed from inside of parentheses, to avoid 336parsing ambiguities. For example, to set the thickness of a box to 337be twice the default thickness: 338 339~~~~ 340 box thickness 2*(thickness) 341 ### ^^^^^^^^^^^---- must be inside (...) 342~~~~ 343 344The extra parentheses around variables "thickness", "color", and "fill" 345are only required when the values are being read, not when the variable 346name appears on the left-hand size of an assignment. You still do: 347 348~~~~ 349 thickness *= 1.5 350~~~~ 351 352## The "`arc`" object does not actually draw an arc. 353 354The behavior of the "`arc`" object is underspecified in the original 355[BWK paper on PIC][bwk]. Nobody is sure exactly what "arc" is supposed 356to do. Furthermore, arcs seem to be seldom used. 357Splines and lines with a radius at corners are better mechanisms 358for drawing curvy lines in a diagram. For these reasons, and to 359keep the implementation simple, Pikchr does not actually draw an 360arc for the "`arc`" object. Instead it draws a quadratic Bézier 361curve across *approximately* the same path that a true arc would have 362taken. 363 364The 30° dimensional "arc" in the drawing below 365(taken from [a tutorial analysis of a Pikchr script](./teardown01.md)) 366is really a spline. It is close enough to a true 367arc for the purposes of Pikchr. Can you tell the difference? 368 369``` pikchr 370scale = 0.8 371linewid *= 0.5 372circle "C0" fit 373circlerad = previous.radius 374arrow 375circle "C1" 376arrow 377circle "C2" 378arrow 379circle "C4" 380arrow 381circle "C6" 382circle "C3" at dist(C2,C4) heading 30 from C2 383 384d1 = dist(C2,C3.ne)+2mm 385line thin color gray from d1 heading 30 from C2 \ 386 to d1+1cm heading 30 from C2 387line thin color gray from d1 heading 0 from C2 \ 388 to d1+1cm heading 0 from C2 389spline thin color gray <-> \ 390 from d1+8mm heading 0 from C2 \ 391 to d1+8mm heading 10 from C2 \ 392 to d1+8mm heading 20 from C2 \ 393 to d1+8mm heading 30 from C2 \ 394 "30°" aligned below small 395 396X1: line thin color gray from circlerad+1mm heading 300 from C3 \ 397 to circlerad+6mm heading 300 from C3 398X2: line thin color gray from circlerad+1mm heading 300 from C2 \ 399 to circlerad+6mm heading 300 from C2 400line thin color gray <-> from X2 to X1 "distance" aligned above small \ 401 "C2 to C4" aligned below small 402``` 403 404## Discontinued Features 405 406Pikchr deliberately omits some features of legacy PIC for security 407reasons. Other features are omitted for lack of utility. 408 409### Pikchr omits the "`sh`" and "`copy`" statements. 410 411The "`sh`" command provided the script the ability to run arbitrary 412shell commands on the host computer. Hence "`sh`" was just a built-in 413[RCE vulnerability][rce]. Having the ability to run arbitrary shell 414commands was a great innovation in a phototypesetting control 415system for Version-III Unix running on a PDP/11 in 1982, in a 416controlled-access facility. 417But such a feature is undesirable in modern web-facing applications 418accessible to random passers-by on the Internet. 419 420[rce]: https://en.wikipedia.org/wiki/Arbitrary_code_execution 421 422The "`copy`" command is similar. It inserts the text of arbitrary 423files on the host computer into the middle of the PIC-script. 424 425### Pikchr omits "`for`" and "`if`" statements 426 427Pikchr omits all support for branching and looping. Each Pikchr 428statement maps directly into (at most) one graphic object in the 429output. This is a choice made to enhance the security and safety 430of Pikchr (without branching or looping, there is less opportunity 431for mischief) and to keep the language simple and accessible. 432 433To be clear, we *could* in theory implement loops and branches and 434subroutines in Pikchr in a safe way. But doing so would be extra 435complication, both in the implementation and in the mental model that 436is maintained by the user. Hence, in order to keep thing simple 437we choose to omit those features. 438If you need machine-generated code, employ a separate script 439language like Python or TCL to generate the Pikchr script for 440you. 441 442### Pikchr omits the built-in "`sprintf()`" function 443 444The `sprintf()` function has well-known security concerns, and we 445do not want to make potential exploits accessible to attackers. 446Furthermore, the `sprintf()` is of little to no utility in a Pikchr 447script that lacks loops. A secure version of `sprintf()` could be 448added to Pikchr, but doing that would basically require recoding 449a secure `sprintf()` from from scratch. It is safer and easier 450to simply omit it. 451 452### Pikchr omits "`{...}`" subblocks 453 454The "`[...]`" style subblocks are supported and they work just as well. 455