2008年7月2日星期三

Metapost 个人总结

接触metapost是因为latex,到现在metapost的手册也看了几遍了,但真正熟练运用metapost还是因为毕业论文。我的毕业论文是用latex写的,而且是在linux下完成的。linux下有两个比较好的latex编辑器:emacs和kile,但是数据图形的处理却缺乏比较顺手的工具。gnuplot和labplot试用了一下,感觉不是很满意,而以前也学过一段时间的metapost,所以最后决定用metapost来画论文中的数据图。使用metapost一个多月下来才真正发现其强大之处,现总结一下。

一、metapost的编译方式

metapost文件不同的编译可以得到不同的图片格式:eps和pdf。

eps格式编译:mpost foo.mp
pdf格式编译:mptopdf foo.mp

eps图片可以直接插入到latex中,与latex无缝融合。有时需要在eps中插入汉字和特殊字符,这些字符可以使用latex和tex来编译。为了插入汉字,mp文件需要添加相应的设置,如下例:
verbatimtex
%&latex
\documentclass[10pt,a4paper]{article}
\usepackage{CJK,amssymb}
\begin{document}
\begin{CJK}{GBK}{song}
etex;
draw btex 这里是中文宋体 etex;
verbatimtex
\end{CJK}
\end{document}
etex;
end
文件中verbatimtex...etex和btex...etex中间的部分由指定的编译方式来编译,"%&latex"指明了用latex来编译。

latex方式编译:mpost -tex=latex foo.mp

插入汉字后得到的eps图片gv等工具是无法浏览的,因为gv等不支持中文字体。为了能够浏览生成的eps图片,一般在文件的开始加入"prologues :=1"(在linux可能要加入"prologues :=2"),当然也可以"mptopdf foo.mp"编译生成pdf文件浏览。

二、画笔

涉及到画图就要说说画笔,metapost自带了两种画笔:pencircle和pensquare,也可以自己定义画笔:

pen mypen[];
mypen1 = makepen((0, 0.7)--(0.5, 0)--(0, -0.7)--(-0.5, 0)--cycle); %菱形
mypen2 = makepen((-0.5, -0.3)--(0, 0.7)--(0.5, -0.3)--cycle); %三角

画笔必须是封闭图形,划线时按颜色填充画笔。画线时可以根据需要选用画笔,可以设置线条颜色,宽度等:

pickup pencircle scaled 1pt;
draw (0,0)--(1cm,1cm) withcolor red;

或者:

draw (0,0)--(1cm,1cm) withpen mypen1 scaled 1pt withcolor (1,0,0)%支持rgb颜色格式


三、线型

画笔的线型有多种,还可以自己设置独特的线性。如虚线:

darw (0,0)--(1cm,1cm) dashed evenly scaled 2;

自定义线型可以看metapost手册。

四、曲线

metapost除了可以画直线之外,还可以自动画出曲线。上面的例子中(0,0)--(1cm,1cm)表示是直线,而(0,0)..(1cm,1cm)表示是曲线,为贝赛尔曲线。还可以指定曲线经过某点时的方向:

draw (0,0){dir 30}..{dir 45}(1cm,1cm}

五、图形处理

metapost对图形进行处理,如旋转(rotated)、移动(shifted)、缩放(xscaled, yscaled)等。

六、graph宏包

metapost的强大还体现在很多地法,下面主要讲一下graph宏包,因为论文要处理数据曲线,所以要经常用到这个宏包。下面是一个使用graph宏包画数据曲线的例子。

verbatimtex
%&latex
\documentclass[10pt,a4paper]{article}
\usepackage{CJK,amssymb}
\begin{document}
\begin{CJK}{GBK}{song}
\Large
etex;
input graph;
beginfig(1);
draw begingraph(12cm,8cm);%设置图片大小
init_numbers(btex$-$etex, btex$1$etex, btex${\times}$etex, btex${}^-$etex, btex${}^2$etex);%设置坐标轴数字格式,使用latex来处理数字。
setrange(0,5,18,15);%设置横纵坐标范围
path p[];
picture lab[];
pen mypen[];
mypen1 = makepen((0, 0.7)--(0.5, 0)--(0, -0.7)--(-0.5, 0)--cycle); %菱形
mypen2 = makepen((-0.5, -0.3)--(0, 0.7)--(0.5, -0.3)--cycle); %三角
lab1 = btex 温度线膨胀系数/$(10^{-6}/K)$ etex rotated 90;
glabel.lft(image(draw lab1),OUT);
glabel.bot(btex 含水率/\% etex, OUT);
gdata("7-days.txt", $, augment.p1($1, $2););
for x = 0 step 3 until 18:
grid.bot(format("%g", x), x) withcolor 0.80white;
endfor;
for y = 5 step 2.5 until 15:
grid.lft(format("%g", y), y) withcolor 0.80white;
endfor;
gdraw (point 0 of p1
for i= 1 upto length p1:
..point i of p1
endfor) withpen pencircle scaled 1.5pt withcolor red;
for i = 0 step 1 until length p1:
gdraw point i of p1 withpen mypen1 scaled 6pt withcolor red;
endfor;
gdata("14-days.txt", $, augment.p2($1, $2););
gdraw p2 withpen pencircle scaled 1.2pt withcolor green;
for i = 0 step 1 until length p2:
gdraw point i of p2 withpen mypen2 scaled 6pt withcolor green;
endfor;
gdata("28-days.txt", $, augment.p3($1, $2););
gdraw p3 withpen pencircle scaled 1.2pt withcolor blue;
for i = 0 step 1 until length p3:
gdraw point i of p3 withpen pensquare scaled 6pt withcolor blue;
endfor;
gdata("56-days.txt", $, augment.p4($1, $2););
gdraw p4 withpen pencircle scaled 1.2pt withcolor (0.8, 0.3, 0);
for i = 0 step 1 until length p4:
gdraw point i of p4 withpen pencircle scaled 6pt withcolor (0.8, 0.3, 0);
endfor;
lab9 = image(
draw (-25pt, 9pt)--(-3pt, 9pt) withpen pencircle scaled 1.2pt withcolor (0.8, 0.3, 0);
draw (-14pt, 9pt) withpen pencircle scaled 6pt withcolor (0.8, 0.3, 0);
draw btex 56天龄期 etex;
draw (-25pt, 27pt)--(-3pt, 27pt) withpen pencircle scaled 1.2pt withcolor blue;
draw (-14pt, 27pt) withpen pensquare scaled 6pt withcolor blue;
draw btex 28天龄期 etex shifted (0, 18pt);
draw (-25pt, 45pt)--(-3pt, 45pt) withpen pencircle scaled 1.2pt withcolor green;
draw (-14pt, 45pt) withpen mypen2 scaled 6pt withcolor green;
draw btex 14天龄期 etex shifted (0, 36pt);
draw (-25pt, 63pt)--(-3pt, 63pt) withpen pencircle scaled 1.2pt withcolor red;
draw (-14pt, 63pt) withpen mypen1 scaled 6pt withcolor red;
draw btex 7天龄期 etex shifted (0, 54pt););
glabel(image(unfill bbox lab9; draw bbox lab9 withpen pencircle scaled 0.5pt; draw lab9), (15.5,13));
endgraph;
endfig;
verbatimtex
\end{CJK}
\end{document}
etex;
end

上面的例子中需要重点说明的是:

(一)坐标轴的设置

1.标签(label)
"lab1 = btex 温度线膨胀系数/$(10^{-6}/K)$ etex rotated 90;"是定义纵坐标label,然后"glabel.lft(image(draw lab1),OUT);"画出此标签,定义标签时用到了旋转"rotated 90"。

2.stick

for x = 0 step 3 until 18:
grid.bot(format("%g", x), x) withcolor 0.80white;
endfor;
for y = 5 step 2.5 until 15:
grid.lft(format("%g", y), y) withcolor 0.80white;
endfor;

画出网格,指出stick方向,网格线为0.80white颜色。

(二)曲线

1.数据读入
曲线的数据点通过文件读入:

gdata("7-days.txt", $, augment.p1($1, $2););

"augment.p1($1, $2);"表示将数据文件"7-days.txt"中第一、第二列数据组成的点作为path p1曲线上的点,即第一列各点为x值,第二列各点为y值。

2.数据点标记

有时需要标记数据点,方法如下:

for i = 0 step 1 until length p1:
gdraw point i of p1 withpen mypen1 scaled 6pt withcolor red;
endfor;

这是在曲线p1的每个数据点画上直径为6pt的红色小点。

3.曲线格式

由augment得到的曲线draw得到的各点之间是直线连接的,如果想曲线连接,可以做如下处理:

gdraw (point 0 of p1
for i= 1 upto length p1:
..point i of p1
endfor) withpen pencircle scaled 1.5pt;

(三)图例

图例是一个pic,内容可以自己定义:

lab9 = image(
draw (-25pt, 9pt)--(-3pt, 9pt) withpen pencircle scaled 1.2pt withcolor (0.8, 0.3, 0);
draw (-14pt, 9pt) withpen pencircle scaled 6pt withcolor (0.8, 0.3, 0);
draw btex 56天龄期 etex;
draw (-25pt, 27pt)--(-3pt, 27pt) withpen pencircle scaled 1.2pt withcolor blue;
draw (-14pt, 27pt) withpen pensquare scaled 6pt withcolor blue;
draw btex 28天龄期 etex shifted (0, 18pt);
draw (-25pt, 45pt)--(-3pt, 45pt) withpen pencircle scaled 1.2pt withcolor green;
draw (-14pt, 45pt) withpen mypen2 scaled 6pt withcolor green;
draw btex 14天龄期 etex shifted (0, 36pt);
draw (-25pt, 63pt)--(-3pt, 63pt) withpen pencircle scaled 1.2pt withcolor red;
draw (-14pt, 63pt) withpen mypen1 scaled 6pt withcolor red;
draw btex 7天龄期 etex shifted (0, 54pt););

定义完后画出即可:

glabel(image(unfill bbox lab9; draw bbox lab9 withpen pencircle scaled 0.5pt; draw lab9), (15.5,13));

其中"draw bbox lab9"是给图例加一个框。


(四)双坐标轴

有时需要定义双坐标轴,下面是一个例子:

verbatimtex
%&latex
\documentclass[10pt,a4paper]{article}
\usepackage{CJK}
\begin{document}
\begin{CJK}{GBK}{song}
etex;
input graph;
beginfig(1);
draw begingraph(10cm,7cm);
init_numbers(btex$-$etex, btex$1$etex, btex${\times}$etex, btex${}^-$etex, btex${}^2$etex);
path p[];
picture lab[];
lab1 = btex 大气温度/$\,^{\circ}\mathrm{C}$ etex rotated 90;
lab2 = btex 太阳散射辐射照度/($W/m^2)$ etex rotated -90;
lab3 = btex 大气温度 etex;
lab4 = btex 散射辐射照度 etex;
setrange(0,26,24,38);
glabel.bot(btex 时间/$h$ etex,OUT);
glabel.lft(image(draw lab1),OUT);
gdata("rad-temp.txt", $, augment.p1($1,$2); augment.p2($1,$3););
for x = 0 step 3 until 24:
grid.bot(format("%g", x), x) withcolor 0.80white;
endfor;
for y = 26 step 2 until 38:
grid.lft(format("%g", y), y) withcolor 0.80white;
endfor;
gdraw (point 0 of p1
for i= 1 upto length p1:
..point i of p1
endfor) withpen pencircle scaled 1pt;
setcoords(linear, linear);
setrange(0,0,24,180);
for y = 0 step 30 until 180:
itick.rt(format("%g", y), y) withcolor 0.8white;
endfor;
gdraw p2 withpen pencircle scaled 1pt;
glabel.rt(image(draw lab2), OUT);
pickup pencircle scaled 5pt;
for i = 0 step 1 until length p2:
gdraw point i of p2;
endfor;
pickup defaultpen;
p3 = (0, 0)--(3.2cm, 0)--(3.2cm, 1.5cm)--(0, 1.5cm)--cycle;
glabel.urt(image(unfill p3; draw p3), 0, 135);

glabel.urt(image(draw (-0.2cm,0.2cm)--(-0.9cm,0.2cm) withpen pencircle scaled 1.0pt;
draw lab3),(0,157));
glabel.urt(image(draw (-0.2cm,0.2cm)--(-0.9cm,0.2cm) withpen pencircle scaled 1.0pt;
draw (-0.6cm,0.2cm) withpen pencircle scaled 5pt; draw lab4),(0,142));

endgraph;
endfig;
verbatimtex
\end{CJK}
\end{document}
etex;
end

其中重要的设置为:setcoords和setrange这两个函数。



没有评论: