MetaPost 学习笔记

MetaPost 用法

数据类型

MetaPost 提供 9 种数据类型,分别是:

数字

默认数据类型是 numeric,因此如果变量不作声明的话,就被视作 numeric。

n := 10;

相当于:

numeric n;
n := 10;

其他类型的变量,必须先声明再赋值,如:

pair p;
p := (0,0);

numeric 的取值范围是 [1/65536, 4096]

赋值

赋值符号(assignment operator)是 :=,例如:

u := 1cm;
a := a+1;

= 用在求解线性方程(linear equations)的场合,例如:

a + b = 3; 
2a = b + 3;

MetaPost 可以算出 a = 2b = 1,再如:

a = 2;
a = b;

上例中 MetaPost 会算出 b = 2。下例可以看到,MetaPost 计算出两条直线的交叉点,并在其上添加标注:

u := 0.7cm;

pair a,b,c,d;
a := (0,0); b := (6u,4u); c := (1u,3u); d := (6u,1u);
draw a--b; draw c--d;

pair x;
x = whatever[a,b] = whatever[c,d];
dotlabel.top(btex x etex, x);

上例中 whatever 引入了一个匿名变量,因我们不需要要在之后引用这个变量。

很多时候,MetaPost 的代码里 :== 是混用的,但由于 = 不是赋值符,因此 a = a + 1 会导致错误(输出 inconsistent equation),因一个数不可能等于它自己加 1。

变量

MetaPost 预定义了大量的隐形变量,例如:x, y, z

z = (x,y)
xpart z = x
ypart z = y

其他预定义变量:

origin = (0,0)

下例直接使用了这些变量,dotlabels 会在 z<suffix> 处添加标注:

z0 = origin;
z1 = z0 + 40 dir 30;
z2 = z1 + 20 dir 120;
dotlabels.top(0, 1, 2);

几种等价的变量写法:

p3 = p[3]
p.3.4 = p[3][4]
1cm = 1 * cm
.8white = 0.8 * white

布尔值

boolean 的取值是 truefalse。这些操作符会返回 boolean 值:

=, <>, <, <=, >, =>, not, and, or

注意不等于写作 <>大于等于写作 =>,逻辑取反是 not

有 2 种谓词(predicates)。1. 数据类型的名字可作为谓词,如:

color c;
path p;

2. 其他谓词:

odd n (n is odd)
cycle p (path p is a cycle)
known x (x has a value)
unkown x (x doesn't have a value)

判断 n 是奇数用 odd n,判断 n 是偶数用 not odd n

颜色

MetaPost 支持 RGB 和 CMYK 格式的颜色。其中 RGB 预定义的几种颜色:

black, white, red, green, blue
black = (0, 0, 0)
white = (1, 1, 1)
red = (1, 0, 0)
green = (0, 1, 0)
blue = (0, 0, 1)

RGB 调色板

u = 0.3cm;
pickup pencircle scaled 8bp;
for i=0 upto 10:
  for j=0 upto 10:
    draw(i*u, j*u) withcolor(0, i*0.1, j*0.1);
    draw(i*u + 12u, j*u) withcolor(i*0.1, 0, j*0.1);
    draw(i*u + 24u, j*u) withcolor(i*0.1, j*0.1, 0);
  endfor
endfor

CMYK 是彩印时使用的一种颜色模式,四种标准颜色是 Cyan(青色)、Magenta(品红色)、Yellow、Black。CMYK 是减色模式,而 RGB 是加色模式。MetaPost 预定义的 CMYK 颜色:

black = (1, 1, 1, 1)
white = (0, 0, 0, 0)

路径(Path)

路径可以是直线或曲线或它们的组合。draw 可以绘制一条路径。

绘制直线:

draw (0,0)--(20,0)--(20,20)--(40,20)--(40,0)--(60,0);

不同风格的直线:

draw (0,30)--(120,30) dashed withdots;
draw (0,20)--(120,20) dashed evenly;
draw (0,10)--(120,10) dashed evenly shifted (3,3);
draw (0,0)--(120,0) dashed evenly scaled 3;

绘制曲线:

draw fullcircle scaled 50;
draw halfcircle scaled 40;
draw quartercircle scaled 30;
draw quartercircle scaled 30 rotated 180;
filldraw fullcircle scaled 20 withcolor (0.15, 0.68, 0.38);

MetaPost 的坐标系

MetaPost 的坐标系和我们直观理解的一样,X 轴从原点向右延伸,Y 轴从原点向上延伸。

drawarrow (0,0) -- (0,50);
drawarrow (0,0) -- (50,0);
dotlabel.llft(btex (0,0) etex, (0,0));
label.rt(btex X etex, (50,0));
label.top(btex Y etex, (0,50));

MetaPost 的默认尺寸单元是 Postscript point。Postscript point 也叫做 big point 因为它比针式打印机的点稍大一些。几种预定义尺寸:

1 bp = 1/72 inches
1 pt = 1/72.27 inches
1 pc = 1/6 inches
cm, mm, in

在八个位置加标签:上 top、下 bot、左 lft、右 rt;右上 urt、左上 ulft、右下 lrt、左下 llft。

pair a,b,c,d;
a := (0,0); b := (2cm,0); c := (2cm,2cm); d := (0,2cm);
draw a--b--c--d--cycle;
dotlabel.top(btex $top$ etex, 1/2[d,c]);
dotlabel.bot(btex $bot$ etex, 1/2[a,b]);
dotlabel.lft(btex $lft$ etex, 1/2[a,d]);
dotlabel.rt(btex $rt$ etex, 1/2[b,c]);
dotlabel.ulft(btex $ulft$ etex, d);
dotlabel.urt(btex $urt$ etex, c);
dotlabel.llft(btex $llft$ etex, a);
dotlabel.lrt(btex $lrt$ etex, b);

MetaPost Box

注意:需要先引入 Box Packages 才能使用 Box。

drawboxed() 画一个盒子:

boxit.a(btex $box$ etex);
drawboxed(a);

画两个并排着的盒子:

boxjoin(a.e=b.w)
boxit.a(btex $A$ etex);
boxit.b(btex $B$ etex);
drawboxed(a,b);

画两个并排着的盒子中间间隔 1cm:

boxit.a(btex $A$ etex);
boxit.b(btex $B$ etex);
b.w = a.e + (1cm,0); 
drawboxed(a,b);

从盒子 A 指向盒子 B:

boxit.a(btex $A$ etex);
boxit.b(btex $B$ etex);
b.w = a.e + (1cm,0); 
drawboxed(a,b);
drawarrow a.e -- b.w;

两个盒子相互指向对方

boxit.a(btex $A$ etex);
boxit.b(btex $B$ etex);
b.w = a.e + (1cm,0); 
drawboxed(a,b);
drawarrow a.n {dir 90} .. {dir -90} b.n;
drawarrow b.s {dir -90} .. {dir 90} a.s;

MetaPost 的盒子模型

u := 1cm;

% outside box

pair a,b,c,d;
a = (0,0); b = (0,4u);
c = (4u,4u); d = (4u,0);
draw a--b--c--d--cycle;

% inside box

pair e,f,g,h;
e = (1u,1u); f = (1u,3u);
g = (3u,3u); h = (3u,1u);
fill e--f--g--h--cycle withcolor .8white;

% draw arrows

drawdblarrow 1/2[a,b] -- 1/2[e,f];
drawdblarrow 1/2[b,c] -- 1/2[f,g];
drawdblarrow 1/2[c,d] -- 1/2[g,h];
drawdblarrow 1/2[a,d] -- 1/2[e,h];

label.top(btex $dx$ etex, 1/2[1/2[a,b], 1/2[e,f]]);
label.top(btex $dx$ etex, 1/2[1/2[c,d], 1/2[g,h]]);
label.rt(btex $dy$ etex, 1/2[1/2[b,c], 1/2[f,g]]);
label.rt(btex $dy$ etex, 1/2[1/2[a,d], 1/2[e,h]]);

% draw dots

pickup pencircle scaled 4pt;
dotlabel.urt(btex $c$ etex, 1/2[a,c]);
dotlabel.top(btex $n$ etex, 1/2[b,c]);
dotlabel.bot(btex $s$ etex, 1/2[a,d]);
dotlabel.lft(btex $w$ etex, 1/2[a,b]);
dotlabel.rt(btex $e$ etex, 1/2[c,d]);

dotlabel.lft(btex $sw$ etex, a);
dotlabel.lft(btex $nw$ etex, b);
dotlabel.rt(btex $ne$ etex, c);
dotlabel.rt(btex $se$ etex, d);

MetaPost 宏

MetaPost 宏定义的格式:

def <symbolic token> = <replacement text> enddef

例如我们常见的 fill (填充颜色)就是一个宏:

def fill = addto currentpicture contour enddef

宏可以携带参数(formal parameters),它的形式如:

def rotatedaround(expr z,d) = shifted -z rotated d shifted z enddef;

expr 表明参数 z,d 可以是任意表达式。

如下定义了一个名为 drawit 的宏:每次画一个图向右偏移 2cm。

pair s; 
s := (2cm,0);

def drawit(expr p) =
    draw p shifted s;
    s := s shifted(2cm,0);
enddef;

picture p;
draw btex Berlinix etex;
draw bbox currentpicture withcolor 0.6white;
p := currentpicture;

drawit(p shifted (1cm,1cm));
drawit(p scaled 0.8);
drawit(p yscaled -1);
drawit(p slanted 0.5);

说明

京ICP备12052177号-1