2023 年 04 月 29 日
上一篇:山海经
下一篇:配置文件和模块
每个静物可以有很多门。蜗牛可以从一扇门进去,也可以从另一扇门出来。
有些门有名字,有些门没名字。
有名字的门,简称名门,有 12 个,分别叫子、丑、寅、卯、辰、巳、午、未、申、酉、戌、亥。子门在正北,卯门在正东,午门在正南,酉门在正西。
没有名字的门,可基于有名字的门构建。
假设有一静物 foo
:
picture foo;
框文配置.框.扩充 := 5mm;
foo := 框文("名门");
draw foo;
它的子门在何处?
在 ulcorner foo
和 urcorner foo
的正中,即 foo
的包围盒的左上角顶点和右上角顶点的中点,用 MetaPost 语言可表示为
pair foo.子门;
foo.子门 := .5[ulcorner foo, urcorner foo];
不妨予以验证:
draw foo.子门 withpen pencircle scaled 4pt withcolor darkred;
foo.午门
在 llcorner foo
和 lrcorner foo
的正中,即 foo
的左下角点和右下角点的中点:
foo.午门 := .5[llcorner foo, lrcorner foo];
同理,可写出卯门和酉门:
foo.卯门 := .5[lrcorner foo, urcorner foo];
foo.酉门 := .5[llcorner foo, ulcorner foo];
至此,便有了 4 门:
pair foo.子门, foo.午门, foo.卯门, foo.酉门;
foo.子门 := .5[ulcorner foo, urcorner foo];
foo.午门 := .5[llcorner foo, lrcorner foo];
foo.卯门 := .5[lrcorner foo, urcorner foo];
foo.酉门 := .5[llcorner foo, ulcorner foo];
forsuffixes i = 子门, 午门, 卯门, 酉门:
draw foo.i withpen pencircle scaled 4pt withcolor darkred;
endfor;
注意,上述代码用的不是普通的 for
,而是 forsuffixes
。倘若用普通的 for
,MetaPost 编译器会认为上述代码里变量 i
的值不是合法的表达式而报错退出。
若将上述 forsuffixes
语句改为 for
,只能写为
for i = foo.子门, foo.午门, foo.卯门, foo.酉门:
draw i withpen pencircle scaled 4pt withcolor darkred;
endfor;
基于子、午、卯、酉门以及四个角点,可以再构造出 8 个门:
forsuffixes i = 丑门, 寅门, 辰门, 巳门, 未门, 申门, 戌门, 亥门:
pair foo.i;
endfor;
foo.丑门 := .5[foo.子门, urcorner foo];
foo.寅门 := .5[foo.卯门, urcorner foo];
foo.辰门 := .5[foo.卯门, lrcorner foo];
foo.巳门 := .5[foo.午门, lrcorner foo];
foo.未门 := .5[foo.午门, llcorner foo];
foo.申门 := .5[foo.酉门, llcorner foo];
foo.戌门 := .5[foo.酉门, ulcorner foo];
foo.亥门 := .5[foo.子门, ulcorner foo];
forsuffixes i = 丑门, 寅门, 辰门, 巳门, 未门, 申门, 戌门, 亥门:
draw foo.i withpen pencircle scaled 4pt withcolor darkgreen;
endfor;
于是,便有了十二名门:
由于所有的静物的门皆能仿照上述过程构建,因此,可以定义一个宏,复用上述代码:
def 名门 suffix foo =
forsuffixes i = 子门, 卯门, 午门, 酉门,
丑门, 寅门, 辰门, 巳门, 未门, 申门, 戌门, 亥门:
pair foo.i;
endfor;
foo.子门 := .5[ulcorner foo, urcorner foo];
foo.午门 := .5[llcorner foo, lrcorner foo];
foo.卯门 := .5[lrcorner foo, urcorner foo];
foo.酉门 := .5[llcorner foo, ulcorner foo];
foo.丑门 := .5[foo.子门, urcorner foo];
foo.寅门 := .5[foo.卯门, urcorner foo];
foo.辰门 := .5[foo.卯门, lrcorner foo];
foo.巳门 := .5[foo.午门, lrcorner foo];
foo.未门 := .5[foo.午门, llcorner foo];
foo.申门 := .5[foo.酉门, llcorner foo];
foo.戌门 := .5[foo.酉门, ulcorner foo];
foo.亥门 := .5[foo.子门, ulcorner foo];
enddef;
新建一个框文:
picture 城;
框文配置.框.扩充 := 1cm;
城 := 框文("城");
draw 城;
蜗牛从城的卯门出发,绕城 3/4 圈,由午门入城,绘制其路径:
path 外环路;
numeric a, b, r;
a := bbheight 城; b := bbwidth 城; r := 5mm;
名门 城;
外环路 := 城.卯门 向 (东 r) 向 (北 .5a + r) 向 (西 b + 2r) 向 (南 a + 2r) 向 (东 .5b + r) -- 城.午门;
drawarrowpath 外环路;
结果为
路径太粗了,导致箭头失常,可通过 MetaFun 宏 drawpathoptions
进行调整:
drawpathoptions(withpen pencircle scaled 2pt withcolor darkred);
由于蜗牛尚不知如何自动拐弯,因此上述路径的走位颇为怪异。
让蜗牛自动拐 90 度弯,此事并不难,例如
pair a, b;
a := (0, 0); b := (4cm, 2cm);
% 经转
drawpathoptions(withpen pencircle scaled 2pt withcolor darkgreen);
drawarrowpath a -- (xpart point (length a) of a, ypart b) -- b
% 纬转
drawpathoptions(withpen pencircle scaled 2pt withcolor darkred);
drawarrowpath a -- (xpart b, ypart point (length a) of a) -- b;
xpart
和 ypart
皆为 MetaPost 宏,分别用于获取 pair
对象的 x 和 y 坐标分量。
若分别为经转和纬转定义两个运算符宏,便可实现自动拐弯:
tertiarydef a 经转 b =
a -- (xpart point (length a) of a, ypart b) -- b
enddef;
tertiarydef a 纬转 b =
a -- (xpart b, ypart point (length a) of a) -- b
enddef;
pair a, b; a := (0, 0); b := (4cm, 2cm);
drawarrowpath a 经转 b;
drawarrowpath a 纬转 b;
更复杂一些的直角转弯也不在话下:
pair a, b, c, d;
a := (0, 0); b := (4cm, 2cm); c := (0, 2cm); d := (-1cm, 1cm);
drawpathoptions(withpen pencircle scaled 2pt withcolor darkred);
drawarrowpath a 纬转 b 纬转 c 经转 d;
基于 左转
和 右转
宏,可为蜗牛重新构造一条简洁的外环路:
picture 城;
框文配置.框.扩充 := 1cm;
城 := 框文("城");
draw 城;
numeric r; r := 5mm;
path 郭;
郭 := 城 enlarged (r, r);
名门 城; 名门 郭;
path 外环路;
外环路 := 城.卯门 -- 郭.卯门 经转 郭.子门 纬转 郭.酉门 经转 郭.午门 -- 城.午门;
drawarrowpath 外环路;
有城就有郭。现在的郭,就是外环路吧……
城郭完整的代码:
\usemodule[zhfonts]
\startuseMPgraphic{foo}
tertiarydef a 经转 b =
a -- (xpart point (length a) of a, ypart b) -- b
enddef;
tertiarydef a 纬转 b =
a -- (xpart b, ypart point (length a) of a) -- b
enddef;
numeric 框文配置.框.扩充, 框文配置.框.线粗, 框文配置.框.玩笑;
框文配置.框.扩充 := 4pt;
框文配置.框.线粗 := 4pt;
框文配置.框.玩笑 := 0;
color 框文配置.文字.颜色, 框文配置.框.颜色, 框文配置.框.背景;
框文配置.文字.颜色 := black;
框文配置.框.颜色 := darkgray;
框文配置.框.背景 := lightgray;
path 框文配置.框形;
框文配置.框形 := fullsquare;
vardef 框文 expr a =
save 框, 文;
path 框; picture 文;
文 = textext(a);
框 := fullsquare
xscaled (bbwidth 文) yscaled (bbheight 文)
enlarged (框文配置.框.扩充 * (1, 1));
框 := 框文配置.框形 xscaled (bbwidth 框) yscaled (bbheight 框); % <= 关键之处
if 框文配置.框.玩笑 > 0: 框 := 框 randomized 框文配置.框.玩笑; fi;
image(fill 框 withcolor 框文配置.框.背景;
draw 框 withpen pencircle scaled 框文配置.框.线粗 withcolor 框文配置.框.颜色;
draw 文 withcolor 框文配置.文字.颜色;)
enddef;
def 名门 suffix foo =
forsuffixes i = 子门, 卯门, 午门, 酉门,
丑门, 寅门, 辰门, 巳门, 未门, 申门, 戌门, 亥门:
pair foo.i;
endfor;
foo.子门 := .5[ulcorner foo, urcorner foo];
foo.午门 := .5[llcorner foo, lrcorner foo];
foo.卯门 := .5[lrcorner foo, urcorner foo];
foo.酉门 := .5[llcorner foo, ulcorner foo];
foo.丑门 := .5[foo.子门, urcorner foo];
foo.寅门 := .5[foo.卯门, urcorner foo];
foo.辰门 := .5[foo.卯门, lrcorner foo];
foo.巳门 := .5[foo.午门, lrcorner foo];
foo.未门 := .5[foo.午门, llcorner foo];
foo.申门 := .5[foo.酉门, llcorner foo];
foo.戌门 := .5[foo.酉门, ulcorner foo];
foo.亥门 := .5[foo.子门, ulcorner foo];
enddef;
drawpathoptions(withpen pencircle scaled 2pt withcolor darkred);
picture 城;
框文配置.框.扩充 := 1cm;
城 := 框文("城");
draw 城;
numeric r;
r := 5mm;
path 郭;
郭 := 城 enlarged (r, r);
名门 城; 名门 郭;
path 外环路;
外环路 := 城.卯门 -- 郭.卯门 经转 郭.子门 纬转 郭.酉门 经转 郭.午门 -- 城.午门;
drawarrowpath 外环路;
\stopuseMPgraphic
\startTEXpage[offset=4pt]
\useMPgraphic{foo}
\stopTEXpage