PiQ Lang Primer
PiQ Lang is the language ParametrIQ designs are written in. A ParametrIQ CAD drawing is a PiQ Lang program: the
.piq file is a list of PiQ Lang lines, and everything you draw with the mouse is saved as them. This primer takes
you through the language from the start. It isn't a complete reference: each section links to the pages that are.
Where to run it
- The playground: type a program and run it. Each statement is run in turn, and the playground shows
what each value is, or what went wrong. A program that draws can be downloaded as a
.piqfile and opened in ParametrIQ CAD. - The Interactive window of ParametrIQ CAD: type a line, press Enter, and it is run on the drawing you have open.
- A
.piqfile: ParametrIQ CAD runs it, statement by statement, when it opens it.
Each example below is a program on its own. After it comes what the playground shows when you run it.
Lines and comments
A program is a list of statements: assignments, commands and expressions. They run from the top down, each when it
is reached. A statement usually has a line to itself; to put several on one line, separate them with ;.
A statement goes on over the end of a line while a bracket ((, [ or {) is still open. So a long one can be split
after a comma, inside its brackets.
// starts a comment, which runs to the end of the line and is skipped. A comment can have the line to itself, or
follow a statement. A // inside the quotes of a string is part of the string.
// The length of the lid
LID=120 // in millimetres
FLAP=40; TUCK=FLAP/2
SIZES={"A4":210,
"A3":297}
LID
SIZES["A3"]
LID → 120
SIZES["A3"] → 297
Typing an expression on its own, as on the last two lines, shows it with its value. PRINT shows
several values at once, each with its expression as the playground reads it back (so 7/2 is shown as 3 1/2).
Values
Numbers
A number can be written as a whole number, a decimal, a fraction, a mixed number, or in feet and inches.
12
0.5
1/4
2 1/4
3'6"
12 → 12
0.5 → 0.5
1/4 → 1/4
2 1/4 → 2 1/4
3'6" → 3'6"
Fractions stay exact while you work with fractions and whole numbers, and show as mixed numbers; so do the functions
that can keep them exact (ABS, MIN, ROUND, SQRT(9/16) …). Once a decimal joins in, the result is a decimal:
1/3 + 1/6
1/3 + 1
7/2
7.0/2
1/3 + 1/6 → 1/2
1/3 + 1 → 1 1/3
7/2 → 3 1/2
7.0/2 → 3.5
Feet and inches stay feet and inches when added to feet and inches, and when multiplied or divided by a whole number or a fraction; divided by feet and inches, they give how many times one goes into the other. A decimal turns them into the drawing's units (millimetres in the playground), and so does a plain number added to them in a millimetre drawing (in a drawing in inches, the number is inches). TOMM and TOIN convert between millimetres and inches.
1'6" + 6"
2 * 1'
3' / 4
3' / 6"
2.5 * 1'
TOMM(1)
TOIN(25.4)
1'6" + 6" → 2'
2 * 1' → 2'
3' / 4 → 9"
3' / 6" → 6
2.5 * 1' → 762
TOMM(1) → 25.4
TOIN(25.4) → 1
See Numbers for every way of writing one.
Text
Text (a string) is written in double quotes. + joins text, and a number joined to text becomes text. Inside the
quotes, \n starts a new line, \" is a quote and \\ a backslash.
"Panel " + "A"
"W=" + 5
"Panel " + "A" → "Panel A"
"W=" + 5 → "W=5"
TRUE and FALSE
The two truth values, written TRUE and FALSE (in any case: true is TRUE). Comparisons give them, and
&&, || and ! combine them.
5 > 3
1 == 1.0
TRUE && !FALSE
5 > 3 → TRUE
1 == 1.0 → TRUE
TRUE && !FALSE → TRUE
Positions
A position is an X and a Y in square brackets: [250,200]. Positions can be added and subtracted, multiplied by a
number, and measured. .X and .Y take them apart.
[3,4] + [1,1]
2 * [3,4]
DISTANCE([0,0],[3,4])
[3,4].Y
[3,4] + [1,1] → [4,5]
2 * [3,4] → [6,8]
DISTANCE([0,0],[3,4]) → 5
[3,4].Y → 4
A position subtracted from another is the step between them: a direction. ANGLE gives a direction's angle, and NORMALIZE makes it 1 long.
Variables
A variable is a name for a value, made with =:
WIDTH=50
DEPTH=WIDTH/2
PRINT WIDTH, DEPTH
WIDTH → 50
DEPTH → 25
Definitions, not copies
A variable keeps its definition, not the value it had when it was made. DEPTH=WIDTH/2 means "half of WIDTH",
always. Change WIDTH, and DEPTH follows:
WIDTH=50
DEPTH=WIDTH/2
WIDTH=80
DEPTH
DEPTH → 40
This is what makes a design parametric: a whole drawing is defined from a few variables, and changing them redraws it. A variable can be given a new definition at any time, even one of another kind (text where there was a number).
Because lines run in order, a name must be defined before a line uses it. And a definition can't go round in a
circle (A=B+1 when B is defined from A): it is refused, and the variable keeps its old definition.
B=A+1
A Not Defined
Names
A name is letters, digits and _, starting with a letter or _. Names aren't case sensitive, and are shown in
capitals: width and WIDTH are the same variable.
A name can't be one of the language's own words. Some of those are short and easy to pick by accident: X and Y
(the parts of a position), and every command and function name (LINE, ARC, SIN, LEFT, SCALE, TEXT …).
Choose names that say what they are (FLAP, LID_DEPTH, X1) and you won't meet them.
y=2
line 1:1 unexpected token
Labels
A variable can have a label, saying what it is for, after a comma. ParametrIQ CAD shows it in the Variables panel, and asks for it by that name.
FLAP=40,"Tuck flap depth"
LABEL changes a label later, and RENAME renames a variable everywhere it is used.
Size and system variables
Every drawing starts with the box's size, L, W and D (length, width and depth: ordinary variables, yours to
change), and with system variables, whose names begin with #: settings such as the units (#U) and the current
layer, constants (#PI), and the defaults for dimensions. They are listed in Variables.
L
W
D
#U
#PI
L → 500
W → 400
D → 300
#U → MILLIMETERS
#PI → 3.141593
Operators and functions
The usual arithmetic, with *, / and % before + and -, and brackets first. % is the remainder after
dividing. It has the sign of the number you divide by, so A % 360 is an angle from 0 up to 360 whatever A is.
2+3*4
(2+3)*4
-2*3
17 % 5
-30 % 360
2+3*4 → 14
(2+3)*4 → 20
-2*3 → -6
17 % 5 → 2
-30 % 360 → 330
There is no power operator: use POW (^ means either, but not both, for TRUE and FALSE).
Comparisons (<, <=, >, >=, ==, !=) give TRUE or FALSE. Decimals are seldom exactly equal after
arithmetic, so ~= compares them to within a tiny margin:
0.1+0.2 == 0.3
0.1+0.2 ~= 0.3
0.1+0.2 == 0.3 → FALSE
0.1+0.2 ~= 0.3 → TRUE
Choosing
test ? a : b is a when the test is TRUE, and b otherwise. SWITCH looks a value
up in a dictionary or a list, with a value for when it isn't there.
SIZE=7
SIZE > 5 ? "large" : "small"
SWITCH(SIZE,{1:"one",7:"seven"},"other")
SWITCH(1,{"zero","one","two"},"other")
SIZE > 5 ? "large" : "small" → "large"
SWITCH(SIZE,{1:"one",7:"seven"},"other") → "seven"
SWITCH(1,{"zero","one","two"},"other") → "one"
Lists and dictionaries
A list is values in braces, {10,20,40}; its items are numbered from 0, so WIDTHS[0] is the first. A dictionary is
keys and values, {"A4":210,"A3":297}, and PAPER["A3"] is the value for a key. .COUNT is how many there are.
WIDTHS={10,20,40}
WIDTHS[2]
WIDTHS.COUNT
PAPER={"A4":210,"A3":297}
PAPER["A3"]
PAPER.KEYS
WIDTHS[2] → 40
WIDTHS.COUNT → 3
PAPER["A3"] → 297
PAPER.KEYS → {"A4","A3"}
Functions
A function is called with its arguments in brackets: SQRT(2), MAX(A,B). There are functions for numbers
(ROUND, MIN, CLAMP …), for angles and
trigonometry, for positions and directions, and for points on elements. The trigonometry functions work in radians;
DTOR and RTOD convert from and to degrees.
SQRT(2)
MAX(3,7)
ROUND(3.5)
SIN(DTOR(30))
RTOD(ANGLE([1,1]))
SQRT(2) → 1.414214
MAX(3,7) → 7
ROUND(3.5) → 4
SIN(DTOR(30)) → 0.5
RTOD(ANGLE([1,1])) → 45
Every function is listed, with examples, in Expressions. You can write functions of your own too: see Functions of your own, below.
Points
A drawing is built on points, named P and a number. Every drawing starts with four, the corners of the box:
P0 at the origin, P1 L to its right, P2 W above P1, and P3 to the left of P2.
P0
P1
P2
P3
P0 → [0,0]
P1 → [500,0]
P2 → [500,400]
P3 → [0,400]
A new point is made like a variable, and, like a variable, keeps its definition. Define it from other points and it follows them:
P4=RIGHT(P0,100)
P5=UP(P4,W/2)
P6=POLAR(P5,45,50)
P7=ALONG(P0,P2,100)
L=600
P1
P5
P1 → [600,0]
P5 → [100,200]
LEFT, RIGHT, UP and DOWN go straight across or up; POLAR goes at
an angle (in degrees) and a distance; ALONG goes a distance towards another point;
LERP a fraction of the way. Setting L moved P1, and P1's definition is
RIGHT(P0,L).
A point can be given a fixed position, P4=[100,0], but a design is only parametric where its points are defined
from others. ParametrIQ CAD's tools make points this way for you: see Snap Modes.
Drawing
Commands add elements to the drawing: lines, arcs, curves, text, dimensions … Each is drawn on points, so it moves when they do.
FLAP=60,"Flap"
P4=UP(P3,FLAP)
P5=UP(P2,FLAP)
POLYLINE @1,[P0,P1,P2,P3],TRUE
LINE @2,P3,P4
LINE @3,P4,P5
LINE @4,P5,P2
P6=[250,200]
P7=LEFT(P6,50)
CIRCLE @5,P7,P6,TRUE
P8=[220,430]
TEXT @6,P8,"Lid",20,0
DIMHORIZONTAL @7,P0,P1,RIGHT
LENGTHOF(@3)
PERIMETEROF(@1)
AREAOF(@5)
LENGTHOF(@3) → 500
PERIMETEROF(@1) → 1800
AREAOF(@5) → 7853.981634
A command is a keyword followed by its arguments, separated by commas. @1, @2 … name the elements, so later
lines can use them; leave the name out and one is chosen for you. A layer number can follow the arguments (LINE P3,P4,1 draws on layer 1, Crease). Where a command takes a point, it takes a point's name: define the point first
(P7=LEFT(P6,50)), then draw on it.
Elements can be measured, and points defined on them, so a point can stay at the middle of a line or where two lines cross: LENGTHOF, ONELEMENT, CROSSING, CENTEROF and the rest of Points on elements.
P4=[250,0]
P5=[250,400]
LINE @1,P0,P2
LINE @2,P4,P5
P6=CROSSING(@1,@2,0)
P6
ONELEMENT(@1,0.5)
@1.END
P6 → [250,200]
ONELEMENT(@1,0.5) → [250,200]
@1.END → [500,400]
Every command is listed in Interactive, with the settings every element can have (layer, line width, hidden …) in Element settings.
A whole design
Put together, a few variables define a whole part. This is the blank for a tray: a base L by W, creased, with
a wall WALL high on each side, its outline cut. Change any size and the blank is redrawn to suit: here the length.
WALL=40,"Wall height"
// the outer corners of the four walls
P4=DOWN(P0,WALL)
P5=DOWN(P1,WALL)
P6=RIGHT(P1,WALL)
P7=RIGHT(P2,WALL)
P8=UP(P2,WALL)
P9=UP(P3,WALL)
P10=LEFT(P3,WALL)
P11=LEFT(P0,WALL)
// the base, on the Crease layer (1); the outline, on the Cut layer (0)
POLYLINE @1,[P0,P1,P2,P3],TRUE,1
POLYLINE @2,[P0,P4,P5,P1,P6,P7,P2,P8,P9,P3,P10,P11],TRUE,0
L=300
PERIMETEROF(@1)
PERIMETEROF(@2)
PERIMETEROF(@1) → 1400
PERIMETEROF(@2) → 1720
Nothing in it is at a fixed position except P0: every corner is worked out from the box's size and WALL, so the
blank is right for any tray.
Moving points
TRANSFORM moves points by a transformation: a shift, a turn, a scale, a mirror. The points are redefined from where they were, so they still follow what they were defined from.
P4=[100,100]
P5=[200,100]
TRANSFORM [P4,P5],TRANSLATE([0,50])
P4
P5
P4 → [100,150]
P5 → [200,150]
Loops and choices
FOR, FOREACH and WHILE repeat a block of
statements, between BEGIN and END, and IF chooses one. Inside a block, statements run one after
another, and each element is made from the points as they are at that moment, so a loop can draw a row of things on
one point moved each time round. A block stays parametric: it runs again whenever the drawing changes, so here a
longer box has more holes.
// a hole every 60 along the middle of the box, as many as fit
FOR I = 30 TO L - 30 STEP 60 BEGIN
P10 = [I, W/2]
P11 = RIGHT(P10, 10)
CIRCLE P11, P10, TRUE
END
IF L > 400 THEN BEGIN
LINE P0, P2
END
What a block sets is its own (its P10 isn't the drawing's), and what it makes belongs to the block. See Blocks and control flow.
Functions of your own
FUNCTION defines a function of your own. The simplest is a single expression of its arguments, and it is used like any other function, anywhere an expression goes:
FUNCTION HYPOT(A,B) = SQRT(A*A+B*B)
HYPOT(3,4)
DIAGONAL=HYPOT(L,W)
DIAGONAL
HYPOT(3,4) → 5
DIAGONAL → 640.312424
DIAGONAL is defined by the function, so it follows the box as L and W change.
A function can also have a block, between BEGIN and END, that runs a statement at a time: it can set its own
variables, choose with IF, loop, call other functions or itself, and give back its value with RETURN:
FUNCTION FACTORIAL(N) BEGIN
IF N <= 1 THEN BEGIN RETURN 1 END
RETURN N*FACTORIAL(N-1)
END
FACTORIAL(6)
FACTORIAL(6) → 720
A function can make geometry as well. Its points and variables are its own (its P1 isn't the drawing's P1), and CALL draws what it makes, again whenever the drawing changes. This one draws a cross that follows P2 wherever it moves:
FUNCTION MARK(C,S) BEGIN
P1 = C+[-S,0] ; P2 = C+[S,0] ; P3 = C+[0,-S] ; P4 = C+[0,S]
LINE P1, P2
LINE P3, P4
END
CALL MARK(P2,20)
A function sees the drawing's variables (L, W …) as they are when it is called, and the other functions. It can be
used before it is defined, and defining it again with the same name replaces it.
JavaScript functions
A function can be written in JavaScript instead, between <<< and >>>, for calculations
and geometry that are easier in a general-purpose language. It is called, drawn with CALL, and saved just as any
other function is, and it is given the drawing's variables and points, and functions to make elements:
FUNCTION RECTAREA(a,b) JAVASCRIPT <<< return a * b >>>
RECTAREA(L,W)
RECTAREA(L,W) → 200000
The playground has longer examples of both: Functions of your own and JavaScript functions.
Drawings within drawings
A drawing can include another as a part: IMPORT reads a .piq file and places it, and
the drawing keeps it as a PART. The part's own variables with the same names as the drawing's,
such as L, take the drawing's values, so a lid drawn for any box fits this one. A
SWITCHPART holds several parts and shows the one a variable picks.
Where next
- Expressions: every operator and function, with examples.
- Interactive: every command.
- ParametrIQ CAD: drawing with the mouse, in the same language.