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`" &lrarr; "`invis`"
280  *  "`first`" &lrarr; "`1st`"
281  *  "`previous`" &lrarr; "`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&deg; 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&deg;" 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