Back to TILs

pikchr

Date: 2023-01-15Last modified: 2023-03-27

Table of contents

Introduction

Pikchr (pronounced like “picture”) is a PIC-like markup language for diagrams in technical documentation. Pikchr is designed to be embedded in fenced code blocks of Markdown (or in similar mechanisms in other markup languages) to provide a convenient means of showing diagrams.

A pikchr fork with some new objects can be found at https://github.com/geraldolsribeiro/pikchr:

A online editor at https://pikchr.org/home/pikchrshow.

Sample

Pikchr sample from official documentation.
Fig. 1Pikchr sample from official documentation.

The figure above was generated by the following code:

topmargin += 1mm
margin = 5mm
arrow right 200% "Markdown" "Source"
box rad 10px "Markdown" "Formatter" "(markdown.c)" fit
arrow right 200% "HTML+SVG" "Output"
arrow <-> down 70% from last box.s
box same "Pikchr" "Formatter" "(pikchr.c)" fit

Image

Image object.
Fig. 2Image object.

The figure above was generated by the following code:

FF: image "/icon/firefox.svg" width 1 height 1
arrow
image "/icon/chrome.svg" width 1 height 1
arrow
image "/icon/brave.svg" width 1 height 1
down
arrow down then right until even with FF then up
Link object.
Fig. 3Link object.

The figure above was generated by the following code:

box width 1 height 1
link "id1" "Google" "https://google.com" width 64px height 64px 
box width 1 height 1
link "id2" "Disney" "https://disney.com" width 32px height 32px
box width 1 height 1
link "id3" "Amazon" "https://amazon.com" width 1 height 1 fill blue
box width 1 height 1
link "id4" "Geraldo" "https://geraldo.dev"
Link object with image object
Fig. 4Link object with image object

The figure above was generated by the following code:

FF: image "/icon/firefox.svg" "https://www.mozilla.org/firefox/" width 1 height 1
arrow
image "/icon/chrome.svg" "https://www.google.com/chrome/" width 1 height 1
arrow
image "/icon/brave.svg" "https://brave.com/" width 1 height 1
down
arrow down then right until even with FF then up

Ark

This is my attempt to provide a more precise arc command.

Ark
Fig. 5Ark

The figure above was generated by the following code:

margin = 0.1
// Axis
line from 0,0 to 0,2 color purple
line from 0,0 to 2,0 color purple
// Approximate circle with 4 arcs
arcrad = 2
arc to 0,2 color red
arc to -2,0 color green
arc to 0,-2 color orange
arc to 2,0 color blue
// Macro ark(radius, startAngle, endAngle)
define ark {
  ArkCenter: text
  $arkNumberOfSegments = 5
  $arkSegmentAngle = ($3-$2)/$arkNumberOfSegments
  ArkP1: line invisible from ArkCenter go $1 heading $2
  ArkP2: line invisible from ArkCenter go $1 heading $2+1*$arkSegmentAngle
  ArkP3: line invisible from ArkCenter go $1 heading $2+2*$arkSegmentAngle
  ArkP4: line invisible from ArkCenter go $1 heading $2+3*$arkSegmentAngle
  ArkP5: line invisible from ArkCenter go $1 heading $2+4*$arkSegmentAngle
  ArkP6:line invisible from ArkCenter go $1 heading $3
  spline from ArkP1.end to ArkP2.end to ArkP3.end to ArkP4.end to ArkP5.end to ArkP6.end $4
  move to ArkCenter
}
// Sample
move to 0,0
ark(0.9, 0, 90, color red)
ark(0.8, 0, 90)
ark(0.7, 0, 90, dashed color green ->)
ark(0.6,45,90, <-> thin)
ark(1.5,30,60)
ark(1.95, 0, 90, color red dotted)
ark(1.95, 90, 180, color blue dotted)
ark(1.95, 180, 270, color orange dotted)
ark(1.95, 270, 360, color green dotted)

Mable diagram

Marble diagram
Fig. 6Marble diagram

The figure above was generated by the following code:

circlerad = 0.1
linewid = 0.2
marble_color = red
define marble { circle $1 color marble_color }
define mark {
  LE: last.end;
  line from LE + (0,0.05) to LE - (0,0.05) then to LE
}
define stop {
  LE: last.end;
  line from LE - (0.05,0.05) to LE + (0.05,0.05);
  line from LE - (0.05,-0.05) to LE + (0.05,-0.05);
  move to LE
}
define mc { marble_color = $1 }
define mr { marble_color = red; marble $1 }
define mg { marble_color = green; marble $1 }
define my { marble_color = yellow; marble $1 }
// ------
S: dot; line; mr "1"; line width 0.6; mg "3"; line; mark; arrow
// ------
move to S - (0,0.3)
dot; line width 0.6; my "2"; line width 0.6; stop; arrow
// ------
move to S - (0,0.8)
dot; line; mr "1"; line; my "2"; line; mg "3"; line; mark; arrow

Filled Polygon

Filled polygon
Fig. 7Filled polygon

The figure above was generated by the following code:

margin = 5mm;
text "Start here" 
dot color green width 200%
# heading zero == north
line thin thin go 6cm heading 60 then down 3cm close fill DarkViolet
Filled polygon fail
Fig. 8Filled polygon fail

The figure above was generated by the following code:

A: (  0, sqrt(3/4))
B: (  0.5, 0)
C: ( -0.5, 0)
margin = 5mm;
line from A to B then to C then to A # fill Brow # 💩 WITHOUT CLOSE IGNORED FILL
circle rad 0.1 at A fill Aqua
circle rad 0.1 at B fill blue
circle rad 0.1 at C fill green
Arrow
Fig. 9Arrow

The figure above was generated by the following code:

margin = 10mm;
dot color blue
line right 2cm then down .5cm then up 1cm right 1cm \
   then up 1cm left 1cm then down .5cm then left 2cm \
   close "with 'close'"
dot color red at last line.end

move to 2.5cm south of last line.start
line right 2cm then down .5cm then up 1cm right 1cm \
   then up 1cm left 1cm then down .5cm then left 2cm \
   then down 1cm "without 'close'"
dot color red at last line.end

Macro

Pikchr macro flag
Fig. 10Pikchr macro flag

The figure above was generated by the following code:

CP: dot invisible;

define flag { 
    line go 1.5 heading $flagAngle \
    then go 0.5 heading $flagAngle + 110 \
    then go 0.5 heading $flagAngle + 250 color $color;
    move to CP;
}
margin = 10mm;
$flagAngle = 00; $color=red;   flag;
$flagAngle = 30; $color=blue;  flag;
$flagAngle = 60; $color=green; flag;
Pikchr macro with parameters
Fig. 11Pikchr macro with parameters

The figure above was generated by the following code:

define mybox {
move right 10mm;
dot color blue;  
box width $1 height $2 radius 2mm color $3;
box $4 width ($1-3mm) height ($2-3mm) at last box radius 1mm;
box width ($1-6mm) height ($2-6mm) at last box;
}
margin = 10mm
mybox(3cm, 4cm, red, "text1")
mybox(2cm, 1cm, blue, "text2")
mybox(4cm,4cm, yellow, "text3")

UTF-8 characters

Pikchr UTF-8 character
Fig. 12Pikchr UTF-8 character

The figure above was generated by the following code:

margin = 1cm
P1: (0,0)
P2: (4cm,3cm)
P3: (4cm,0)
line from P1 to P2 to P3 close dashed thin thin fill purple
line invisible from P1 to P2 "☕☕" above aligned big
line invisible from P2 to P3 " ✍" ljust big
line invisible from P1 to P3 "🍕" below big
dot at P1 fill red
dot at P2 fill blue
dot at P3 fill green
box with .sw at (-1cm,-1cm) width 6cm height 5cm thin color brown radius 2mm
move to (0cm,3cm)
Caption: text "🔵🔴🟠🟡🟢🟣🟤" "🟦🟥🟧🟨🟩🟪🟫" "🛑🔶🔷🔸🔹🔺🔻"

Trigonometric functions

Trigonometric functions
Fig. 13Trigonometric functions

The figure above was generated by the following code:

pi = 3.141592  # define a constant
a = sin(pi)    # 0
b = cos(pi)    # 1
c = abs(-pi)   # 3.141592
d = int(pi)    # 3
e = sqrt(25)   # 5

define trigCircle {
  circle radius 1 color gray
  arrow from (0,-1.2) to (0,1.2) color gray
  arrow from (-1.2,0) to (1.2,0) color gray
}

define angHor  {
  arrow from (0,0) to (sin((90-$1)*pi/180),cos((90-$1)*pi/180)) color purple
  text $2 color purple
}

define angVert {
  arrow from (0,0) to (sin($1*pi/180),cos($1*pi/180)) color green thin thin
  text $2 above rjust color green
}

trigCircle
angHor( 30, "30⁰ = 60 from N" )
angHor( 20, "20⁰ = 70 from N" )
angHor( 10, "10⁰ = 80 from N" )
angVert( 10, "10⁰" )
angVert( 20, "20⁰" )
angVert( 30, "30⁰" )
angVert( 40, "40⁰" )
angVert( 50, "50⁰" )

arrow from (0,0) to (b,a) "Pi" above color red thick thick
Pikchr circle chop
Fig. 14Pikchr circle chop

The figure above was generated by the following code:

margin =  1cm
circlerad = 3mm
circle "a" color brown
circle "b" at 1st circle + (-0.4, -0.6) color green
circle "c" at 1st circle + (0.4, -0.6) color cyan
arrow from 1st circle to 2nd circle chop color purple
arrow from 1st circle to 3rd circle chop color purple
Pikchr path division
Fig. 15Pikchr path division

The figure above was generated by the following code:

margin = 1cm
box width 1cm height 3cm
arrow right from 0 <last box.ne, last box.se> color red "0" small small above
arrow right from 1/3 of the way between last box.ne and last box.se color orange "1/3" small small above
arrow right from 2/3 <last box.ne, last box.se> color blue "2/3" small small above
arrow right from 1 <last box.ne, last box.se> color green "1" small small above

P1: (3cm,-15mm)
P2: (4cm,1.5cm)
P3: (8cm,-15mm)
P4: (4.5cm,1.5cm)

dot at P1 "P1" below rjust
dot at P2 "P2" above rjust
dot at P3 "P3" below ljust
dot at P4 "P4" above ljust

L1: line from P1 to P2
L2: line from P3 to P4

arrow from 1/3 <P1, P2> to 1/3 <P3, P4> color blue
arrow from 2/3 <P1, P2> to 2/3 <P3, P4> color orange
Pikchr spline
Fig. 16Pikchr spline

The figure above was generated by the following code:

margin = 1cm
line dashed right 1 then down .5 left 1 then right 1 thin thin
spline from start of last line right 1 then down .5 left 1 then right 1 color orange
Pikchr block
Fig. 17Pikchr block

The figure above was generated by the following code:

box "1" width 4cm height 4mm
A: [
  box "2" height 6mm width 2cm color green; 
  arrow "3" above width 1cm
  box "4" width 4cm height 1cm color blue
  arrow right 1cm down 1cm "5" above
] with .n at last box.s - (0,15mm)

dot at last [].w "w " rjust  color red
dot at last [] "c" below  color red
dot at last [].e " e" ljust color red
dot at last [].n "n" above color red
dot at last [].s "s" below color red
dot at last [].sw "sw" below color red
dot at last [].nw "nw" above color red
dot at last [].se "se" below color red
dot at last [].ne "ne" above color red

box with .sw at A.sw - (5mm,5mm) width (A.width + 10mm) height (A.height + 10mm) thin thin color orange dotted
Pikchr sub-block
Fig. 18Pikchr sub-block

The figure above was generated by the following code:

margin = 1cm
X: [
  Y: [
    down
    R: box width 5mm height 5mm color red 
    G: box width 5mm height 5mm color green
    B: box width 5mm height 5mm color blue
  ]
  Z: [
    move right 8cm up 0mm
    # up last direction
    A: box width 2cm height 5mm color orange "X.Z.A"
    move up 1mm
    B: box width 2cm height 5mm color brown "X.Z.B"
    move up 1mm
    C: box width 2cm height 5mm color orange "X.Z.C"
  ]
  W: [
    move right 16cm down 0mm
    A: box width 2cm height 5mm color orange "X.W.A"
    move down 1mm
    B: box width 2cm height 5mm color brown "X.W.B"
    move down 1mm
    C: box width 2cm height 5mm color orange "X.W.C"
  ]
]

arrow from X.Y.R right "R" above
arrow from X.Y.G left "G" above
arrow from X.Y.B down " B" ljust

arrow from X.Y.B to X.Z.A chop color purple
arrow from X.Y.B to X.Z.C.w chop color purple
arrow from X.Z.C.e right 5mm then down until even with X.Z.B.e then to X.Z.B.e 

line from X.Z.C to X.W.C thin thin dotted
arrow <-> from X.Z.A to X.W.A color blue chop
Pikchr arc
Fig. 19Pikchr arc

The figure above was generated by the following code:

define r { line 50%; arc; line; arc cw; line; arc cw; line; arc; line 50% }
color = blue
r 
color = green
r
color = red
r
color = orange
r
color = purple
spline -> from (0,0) then to (4,1) then to (8,0)
spline -> from (0,0) then to (4,1.5) then to (8,0)
spline -> from (0,0) then to (4,2) then to (8,0)

Some digrams on internet

Pikchr tailscale diagram. Adapted from https://zellyn.com/2022/02/tailscale-diagram-in-pikchr/
Fig. 20Pikchr tailscale diagram. Adapted from https://zellyn.com/2022/02/tailscale-diagram-in-pikchr/

The figure above was generated by the following code:

topmargin = 1
fontscale = 3
color = 0xc65835 # Orange

define $boxlet {
  oval at -0.18,0 height 0.75 width 0.33 color none fill 0xd6d6d6
  box same as last at -0.12,0.28 height 0.2 width 0.2 radius 0
  box at 0,0 height 1 width 1 color none fill 0x343433 radius 0.2 behind last box
}

define $wall {
  box at 0,0 height 3.1 width 0.76 color none fill 0x496495
  line from -0.38*$1,0.34 to $1*0.38,0.16 to $1*0.38,-0.16 to -0.38*$1,-0.34 close color none fill 0xaec0e0
}

define $wall_info {
  text at last.n + (0.0, 0.5) color 0x496495 $2 bold
  circle at last.n + (0.0,0.3) radius 0.25 thickness 0.05 color 0x5a79a6
  line from (last.x-$1*0.11,last circle.c.y) \
    right $1*0.22 then \
    up $1*0.11 left $1*0.11 then \
    down $1*0.11 right $1*0.11 then \
    down $1*0.11 left $1*0.11 then \
    up $1*0.11 right $1*0.11 \
    color 0x496495 thickness 0.05
}

define $area {
  box at ($1,$2) width $3 height $4 color none fill 0xf9f9f8 radius 0.2
}

# Servers

$area(0.55,0,5.2,3.1)

WS1: [ $boxlet() ] at (0, 0)
text at last.n + (0.0, 0.1) color 0x595857 "VPN Client" above
text at last.s - (0.0, 1) color 0x030303 $2 "Workstation" below

$area(0,-5.25,4.1,3.1)
$area(2.83,-5.25,0.8,3.1)

WS2: [ $boxlet() ] at (0, -5.25)
text at last.n + (0.0, 0.1) color 0x595857 "VPN Client" above
text at last.s - (0.0, 1) color 0x030303 "Workstation" below

$area(8.3,-2.3,6,5.25)

HUB: [ $boxlet() ] at (8.3, -2.4)
text at last.n + (0.0, 0.1) color 0x595857 "VPN Hub"  above
text at last.s - (0.0, 1.78) color 0x030303 "Server" below

$area(16.10,HUB.c.y,4.1,3.1)
$area(13.27,HUB.c.y,0.8,3.1)

WS3: [ $boxlet() ] at (16.10, HUB.c.y)
text at last.n + (0.0, 0.1) color 0x595857 "VPN Client"  above
text at last.s - (0.0, 1) color 0x030303 "EC2 VM" below

[ $wall(1) ] at (3.20,0)
$wall_info(1, "Windows Firewall")

[ $wall(1) ] at (3.2,-5.25)
$wall_info(1, "Office Firewall")

[ $wall(-1) ] at (12.9,HUB.c.y)
$wall_info(-1, "AWS" bold "Security Group" bold "")

dot at WS1.e radius 0.05
dot at WS2.e radius 0.05
dot at WS3.w radius 0.05

line from WS1.e right 4 then down until even with (0,HUB.y+0.2) right 2.3 then right until even with (HUB.w-0.03,0) thickness 0.04 radius 1
line up 0.15 left 0.15 down 0.15 right 0.15 down 0.15 left 0.15 up 0.15 right 0.15 thickness 0.04

line from WS2.e right 4 then up until even with (0,HUB.y-0.2) right 2.3 then right until even with (HUB.w-0.03,0) thickness 0.04 radius 1
line up 0.15 left 0.15 down 0.15 right 0.15 down 0.15 left 0.15 up 0.15 right 0.15 thickness 0.04

line from WS3.w to 0.03 right of HUB.e thickness 0.04
line up 0.15 right 0.15 down 0.15 left 0.15 down 0.15 right 0.15 up 0.15 left 0.15 thickness 0.04

References