PiQ Lang 入门
PiQ Lang 是编写 ParametrIQ 设计所用的语言。一张 ParametrIQ CAD 图纸就是一个 PiQ Lang 程序:
.piq 文件就是由 PiQ Lang 语句行组成的列表,您用鼠标绘制的所有内容都以这些语句行保存。本入门教程将带您从零开始学习这门语言。它并不是完整的参考:每一节都链接到相应的参考页面。
在哪里运行
- Playground:输入程序并运行。每条语句依次运行,Playground 会显示每个值是什么,或者出了什么错。会绘图的程序可以下载为
.piq文件,并在 ParametrIQ CAD 中打开。 - ParametrIQ CAD 的 交互窗口:输入一行,按 Enter,它就会在您打开的图纸上运行。
.piq文件:ParametrIQ CAD 打开它时,会逐条运行其中的语句。
下面的每个示例都是一个独立的程序。示例之后是在 Playground 中运行它时显示的结果。
语句行和注释
程序是一系列语句:赋值、命令和表达式。它们从上到下运行,每条语句在执行到时运行。一条语句通常独占一行;要在一行中放置多条语句,请用 ; 分隔。
只要括号((、[ 或 {)尚未闭合,语句就会延续到下一行。因此,较长的语句可以在括号内的逗号之后换行。
// 表示注释的开始,注释一直到行尾,运行时会被跳过。注释可以独占一行,也可以跟在语句之后。字符串引号内的 // 是字符串的一部分。
// 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
像最后两行那样单独输入一个表达式,会显示该表达式及其值。PRINT 可以一次显示多个值,每个值都附带 Playground 读回的表达式(因此 7/2 显示为 3 1/2)。
值
数字
数字可以写成整数、小数、分数、带分数,或者英尺和英寸。
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"
只用分数和整数进行运算时,分数保持精确,并显示为带分数;能保持精确的函数(ABS、MIN、ROUND、SQRT(9/16) …)也是如此。一旦有小数参与,结果就是小数:
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
英尺和英寸与英尺和英寸相加,或者乘以、除以整数或分数时,仍保持为英尺和英寸;除以英尺和英寸时,得到的是一个值包含另一个值的倍数。小数会将它们转换为图纸的单位(在 Playground 中为毫米),在毫米图纸中与普通数字相加也会如此(在英寸图纸中,该数字表示英寸)。TOMM 和 TOIN 用于在毫米和英寸之间转换。
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
数字的所有写法请参阅数字。
文本
文本(即字符串)写在双引号中。+ 用于连接文本,与文本连接的数字会变成文本。在引号内,\n 表示换行,\" 表示引号,\\ 表示反斜杠。
"Panel " + "A"
"W=" + 5
"Panel " + "A" → "Panel A"
"W=" + 5 → "W=5"
TRUE 和 FALSE
两个真值,写作 TRUE 和 FALSE(不区分大小写:true 即 TRUE)。比较运算会得到它们,
&&、|| 和 ! 用于组合它们。
5 > 3
1 == 1.0
TRUE && !FALSE
5 > 3 → TRUE
1 == 1.0 → TRUE
TRUE && !FALSE → TRUE
位置
位置是写在方括号中的 X 和 Y:[250,200]。位置可以相加、相减、乘以数字,也可以测量。.X 和 .Y 用于取出其分量。
[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
一个位置减去另一个位置,得到的是两者之间的步长:即方向。ANGLE 给出方向的角度,NORMALIZE 将其长度变为 1。
变量
变量是值的名称,用 = 创建:
WIDTH=50
DEPTH=WIDTH/2
PRINT WIDTH, DEPTH
WIDTH → 50
DEPTH → 25
是定义,而不是副本
变量保留的是其定义,而不是创建时的值。DEPTH=WIDTH/2 的意思始终是“WIDTH 的一半”。修改 WIDTH,DEPTH 就会随之变化:
WIDTH=50
DEPTH=WIDTH/2
WIDTH=80
DEPTH
DEPTH → 40
这正是设计具有参数化特性的原因:整张图纸由少数几个变量定义,修改它们就会重绘图纸。变量可以随时被赋予新的定义,甚至可以是另一种类型(原来是数字的地方改为文本)。
由于语句行按顺序运行,名称必须在使用它的行之前定义。而且定义不能形成循环(B 由 A 定义时写 A=B+1):这样的定义会被拒绝,变量保留其原来的定义。
B=A+1
A Not Defined
名称
名称由字母、数字和 _ 组成,以字母或 _ 开头。名称不区分大小写,并以大写显示:width 和 WIDTH 是同一个变量。
名称不能是语言自身的关键字。其中有些关键字很短,容易无意中用到:X 和 Y
(位置的分量),以及所有命令名和函数名(LINE、ARC、SIN、LEFT、SCALE、TEXT …)。选择能说明其含义的名称(FLAP、LID_DEPTH、X1),就不会遇到这些冲突。
y=2
line 1:1 unexpected token
标签
变量可以在逗号之后带一个标签,用于说明其用途。ParametrIQ CAD 会在 变量面板中显示该标签,并以该名称请求输入。
FLAP=40,"Tuck flap depth"
LABEL 用于以后修改标签,RENAME 用于在变量被使用的所有地方重命名该变量。
尺寸变量和系统变量
每张图纸开始时都带有盒子的尺寸 L、W 和 D(长度、宽度和深度:普通变量,可由您修改),以及名称以 # 开头的系统变量:例如单位(#U)和当前图层等设置、常量(#PI),以及尺寸标注的默认值。它们列在变量中。
L
W
D
#U
#PI
L → 500
W → 400
D → 300
#U → MILLIMETERS
#PI → 3.141593
运算符和函数
常用的算术运算:*、/ 和 % 优先于 + 和 -,括号最优先。% 表示除法的余数。其符号与除数相同,因此无论 A 是多少,A % 360 都是 0 到 360 之间的角度。
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
没有幂运算符:请使用 POW(对于 TRUE 和 FALSE,^ 表示二者之一,但不能同时为真)。
比较运算(<、<=、>、>=、==、!=)得到 TRUE 或 FALSE。小数经过算术运算后很少完全相等,因此 ~= 在极小的误差范围内比较它们:
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
选择
test ? a : b 在测试为 TRUE 时为 a,否则为 b。SWITCH 在
字典或列表中查找一个值,并可指定找不到时使用的值。
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"
列表和字典
列表是写在花括号中的值,{10,20,40};其项从 0 开始编号,因此 WIDTHS[0] 是第一项。字典由键和值组成,{"A4":210,"A3":297},PAPER["A3"] 是某个键对应的值。.COUNT 表示项的数量。
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"}
函数
调用函数时,将其参数写在括号中:SQRT(2)、MAX(A,B)。有用于数字的函数(ROUND、MIN、CLAMP …),用于角度和三角函数的函数,用于位置和方向的函数,以及用于图元上的点的函数。三角函数使用弧度;
DTOR 和 RTOD 用于角度与弧度之间的相互转换。
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
所有函数及其示例都列在表达式中。您也可以编写自己的函数:请参阅下文的自定义函数。
点
图纸建立在点之上,点以 P 加数字命名。每张图纸开始时都有四个点,即盒子的四个角:
P0 位于原点,P1 位于其右侧 L 处,P2 位于 P1 上方 W 处,P3 位于 P2 左侧。
P0
P1
P2
P3
P0 → [0,0]
P1 → [500,0]
P2 → [500,400]
P3 → [0,400]
新建点的方法与变量相同,并且与变量一样保留其定义。用其他点来定义它,它就会跟随那些点:
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 和 DOWN 沿水平或竖直方向移动;POLAR 按角度(以度为单位)和距离移动;ALONG 朝另一个点移动一段距离;
LERP 移动全程的一部分。设置 L 使 P1 移动了,因为 P1 的定义是
RIGHT(P0,L)。
点可以被赋予固定位置 P4=[100,0],但只有在点由其他点定义的地方,设计才具有参数化特性。
ParametrIQ CAD 的工具会为您以这种方式创建点:请参阅捕捉模式。
绘图
命令向图纸中添加图元:直线、圆弧、曲线、文字、尺寸标注 … 每个图元都绘制在点上,因此点移动时它也会随之移动。
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
命令由关键字及其后以逗号分隔的参数组成。@1、@2 … 用于为图元命名,以便后面的语句行使用它们;省略名称时会自动为您选择一个。参数之后可以跟一个图层编号(LINE P3,P4,1 绘制在图层 1,即压痕线图层上)。命令需要点的地方,接受的是点的名称:请先定义该点(P7=LEFT(P6,50)),然后在其上绘制。
图元可以测量,也可以在图元上定义点,因此点可以始终位于直线的中点或两条直线的交点处:LENGTHOF、ONELEMENT、 CROSSING、CENTEROF 以及 图元上的点中的其他函数。
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]
所有命令都列在交互窗口中,每个图元都可以具有的设置(图层、线宽、隐藏 …)则列在图元设置中。
完整的设计
组合起来,几个变量就能定义一个完整的零件。下面是一个托盘的展开料:底面为 L × W,带压痕线,四边各有一面高 WALL 的侧壁,其外轮廓为切割线。修改任何尺寸,展开料都会相应重绘:这里修改的是长度。
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
其中除了 P0 之外,没有任何内容位于固定位置:每个角点都由盒子的尺寸和 WALL 计算得出,因此该展开料适用于任何托盘。
移动点
TRANSFORM 按变换移动点:平移、旋转、缩放、镜像。这些点会根据其原来的位置重新定义,因此仍然跟随定义它们的内容。
P4=[100,100]
P5=[200,100]
TRANSFORM [P4,P5],TRANSLATE([0,50])
P4
P5
P4 → [100,150]
P5 → [200,150]
循环和选择
FOR、FOREACH 和 WHILE 重复执行位于
BEGIN 和 END 之间的语句块,IF 则选择执行其中之一。在语句块内,语句依次运行,每个图元都由此刻的点生成,因此循环可以在每次循环时移动同一个点,从而绘制一排图元。语句块仍保持参数化:每当图纸发生变化时它都会重新运行,因此这里盒子越长,孔就越多。
// 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
语句块设置的内容属于它自己(其 P10 不是图纸的 P10),它生成的内容也属于该语句块。请参阅 语句块和控制流。
自定义函数
FUNCTION 用于定义您自己的函数。最简单的函数是由其参数构成的单个表达式,其用法与任何其他函数相同,可用于任何可以使用表达式的地方:
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 由该函数定义,因此当 L 和 W 变化时,它会跟随盒子变化。
函数也可以带有位于 BEGIN 和 END 之间、逐条运行语句的语句块:它可以设置自己的变量,用 IF 进行选择,进行循环,调用其他函数或其自身,并用 RETURN 返回其值:
FUNCTION FACTORIAL(N) BEGIN
IF N <= 1 THEN BEGIN RETURN 1 END
RETURN N*FACTORIAL(N-1)
END
FACTORIAL(6)
FACTORIAL(6) → 720
函数也可以生成几何图形。其点和变量属于它自己(其 P1 不是图纸的 P1), CALL 会绘制它生成的内容,并在图纸每次变化时重新绘制。下面这个函数绘制一个十字,无论 P2 移动到何处,十字都会跟随它:
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)
函数在被调用时可以看到图纸的变量(L、W …)的当前值,以及其他函数。函数可以在定义之前使用,以相同名称再次定义会替换原来的函数。
JavaScript 函数
函数也可以改用 JavaScript 编写,写在 <<< 和 >>> 之间,适用于在通用语言中更容易实现的计算和几何运算。它的调用、用 CALL 绘制以及保存方式都与任何其他函数相同,并且可以获得图纸的变量和点,以及用于创建图元的函数:
FUNCTION RECTAREA(a,b) JAVASCRIPT <<< return a * b >>>
RECTAREA(L,W)
RECTAREA(L,W) → 200000
Playground 中有这两种函数的更长示例:自定义函数和 JavaScript 函数。
图纸中的图纸
一张图纸可以将另一张图纸作为零件包含进来:IMPORT 读取 .piq 文件并放置它,图纸将其保存为 PART。零件中与图纸同名的变量(例如 L)会采用图纸的值,因此为任何盒子绘制的盖都能适配这个盒子。
SWITCHPART 包含多个零件,并显示由某个变量选中的那一个。
下一步
- 表达式:所有运算符和函数,并附有示例。
- 交互窗口:所有命令。
- ParametrIQ CAD:用鼠标绘图,使用的是同一种语言。