想必你也有过这样的疑问,中学数学书上的那些精美的作图是如何画出来的?一直以来,我都想学习一门绘图语言,只是久久未能行动 orz…

闲话少叙,开始学习!

Metapost

介绍什么的,我其实不太关心,所以就不写了……

一个简单的例子

和 C 语言一样,Metapost 有一个源文件xxx.mp,有一个编译器mpost,然后编译之,即得到图片(默认后缀.mps)。

% file:///hello.mp
prologues := 3;
outputtemplate := "%j-%c.mps";
outputformat := "mps";

beginfig(1);
  draw (0,0)--(11,0)--(11,11)--(0,11)--cycle;
endfig;

beginfig(2);
  draw (0,0)..(11,0)..(11,11)..(0,11)..cycle;
endfig;
end

几点说明:

  • Metapost 语句以分号结尾,除了最后的end!
  • 设置prologues:=3会在生成的图像文件.ps中嵌入字体信息,这会增加图片大小
  • 默认单位:PostScript Points (1/72in = 0.352777… mm)

这里简单说一下源码结构,和 LaTeX 一样,Metapost 有导言区,可以做一些设置之类的工作。如hello.mp中前三句就设置了输出文件格式,以及文件名规范等。然后作图部分主要由beginfig--endfig块控制。

beginfig(x);
  draw something;
  draw anything;
endfig;

括号中的 x 替换为数字,类似图片 id. 一个源文件中可以有多个beginfig--endfig块,编译后每个块对应一张图片。

编译hello.mp后即可得到两张图片:

1
2
3
4
5
6
7
$ mpost hello.mp
This is MetaPost, version 2.00 (TeX Live 2018) (kpathsea version 6.3.0)
(/usr/local/texlive/2018/texmf-dist/metapost/base/mpost.mp
(/usr/local/texlive/2018/texmf-dist/metapost/base/plain.mp
Preloading the plain mem file, version 1.005) ) (./hello.mp [1] [2] )
2 output files written: hello-1.mps .. hello-2.mps
Transcript written on tmp.log.
Fig-1
Fig-2

Metapost 按坐标画图非常简单,将坐标点一个一个连起来就行了,注意到--表示直线连接,..表示平滑的曲线连接。和众多编程语言一样,你也可以定义变量方便重复使用。

beginfig(3);
  z0 = (0,0);
  z1 = (60,40);
  z2 = (40,90);
  z3 = (10,70);
  z4 = (30,50);
  draw z0..z1..z2..z3..z4;
endfig
Fig-3

Workflow

使用默认输出格式会产生 PostScript 格式的图片,在 Linux 下可以用 gnome 中的 evince 查看。也可以使用epstopdf转化为 PDF 查看。

Image adapted from ref1

Primitives

变量类型

Metapost 几个常见的类型:

  • pair: (0,0) and (3,4)
  • path: (0,0)–(3,4)
  • pen: (implicit) pen for stroking

比如:

beginfig(0)
  u:=1cm;
  pair a,b; path p; pen mypen;
  a = (0,0); b = (3u,4u);
  p = a--b;
  mypen = pencircle scaled 1mm;
  pickup mypen;
  draw p;
endfig;

All MetaPost variable types:

TypeExample
numeric(default, if not explicitly declared)
pairpair a; a := (2in,3mm);
booleanboolean v; v := false;
pathpath p; p := fullcircle scaled 5mm;
penpen r; r := pencircle;
picturepicture q; q := nullpicture;
transformtransform t; t := identity rotated 20;
colorcolor c; c := (0,0,1); (blue)
cmykcolorcmykcolor k; k := (1,0.8,0,0); (some blue)
stringstring s; s := "Hello";

弯曲控制

我们已经知道使用..可以让 Metapost 在两点之间画出平滑的曲线,尽管它画的很好(默认使用 贝塞尔曲线),但有时候我们往往需要控制哪里该要陡一点,哪里平缓一点。对于这种需求,Metapost 同样提供了精细粒度的控制方法。

beginfig(4);
  for i=0 upto 9:
    draw (0,0){dir 45}..{dir 10a}{6cm, 0};
  endfor
endfig;
Fig-4

可以看出,从图上起点(左边的点),对应坐标 (0,0),引出一族曲线,这些曲线在 (0,0) 处的左极限都是 1,呈现出 45 度角。而在终点 (6cm,0) 处的入角从 0 度到 90 度变化,正如语句中描述的。

未完待续……

Reference

  1. Which Output file should I export from Metapost?
  2. Metapost for Beginners
  3. Learning METAPOST by Doing
  4. Tutorial in MetaPost