每個靜物可以有很多門。蝸牛可以從一扇門進去,也可以從另一扇門出來。
有些門有名字,有些門沒名字。
有名字的門,簡稱名門,有 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;
隱框
框文很好,但有時我不想讓文字帶框,但是有框便於製造名門,權衡利弊,我覺得,應該對框文的定義有所修改,令其可隱藏框、背景,甚至文字:
numeric 框文配置.框.擴充, 框文配置.框.線粗, 框文配置.框.玩笑;
框文配置.框.擴充 := 4pt;
框文配置.框.線粗 := 2pt;
框文配置.框.玩笑 := 0;
numeric 框文配置.框.透明度, 框文配置.背景.透明度,框文配置.文字.透明度;
框文配置.框.透明度 := 0;
框文配置.背景.透明度 := 0;
框文配置.文字.透明度 := 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 框文配置.背景.顏色
withtransparency (1, 1 - 框文配置.背景.透明度);
draw 框 withpen pencircle scaled 框文配置.框.線粗
withcolor 框文配置.框.顏色 withtransparency (1, 1 - 框文配置.框.透明度);
draw 文 withcolor 框文配置.文字.顏色 withtransparency (1, 1 - 框文配置.文字.透明度);)
enddef;
隱藏圖形的原理很簡單,將隱藏的對象設爲完全地透明。例如,
picture 蝸牛;
框文配置.框.透明度 := 1;
框文配置.背景.透明度 := 1;
框文配置.框.擴充 := 5mm;
蝸牛 := 框文("巨型蝸牛");
draw 蝸牛;
框雖然隱藏了,但依然能夠製造名門:
名門 蝸牛;
forsuffixes i = 子門, 午門, 卯門, 酉門:
draw 蝸牛.i withpen pencircle scaled 4pt withcolor darkred;
endfor;
forsuffixes i = 醜門, 寅門, 辰門, 巳門, 未門, 申門, 戌門, 亥門:
draw 蝸牛.i withpen pencircle scaled 4pt withcolor darkgreen;
endfor;
結果爲
歷險
蝸牛的東面 2.5cm 處,有一家有名門的法國餐廳,
picture 餐廳;
框文配置.框.透明度 := 0;
餐廳 := 框文("法國餐廳");
令 餐廳 距 蝸牛 有 (2.5cm, 0);
名門 餐廳;
draw 餐廳;
蝸牛想去餐廳一遊,從自家的卯門出去,進入了餐廳的酉門,
drawarrowpath 蝸牛.卯門 -- 餐廳.酉門;
它沒能進去,餐廳的門太小了。
減肥
MetaFun 的 drawpath
和 drawarrowpath
宏默認使用的筆和顏色,可通過 drawpathoptions
修改,例如
drawpathoptions(withpen pencircle scaled 2pt withcolor darkyellow);
drawarrowpath 蝸牛.卯門 -- 餐廳.酉門;
現在,蝸牛勉強爬入了餐廳,但是餐廳裏空無一人,因爲在第一次試圖進入餐廳時,食客和廚師受驚,從其他 11 門奔逃而去。
最後,蝸牛從餐廳的午門出來,進入了自家的巳門,
drawarrowpath
從 餐廳.午門 向 (南 1cm) 向 (西 (xpart 餐廳.午門 - xpart 蝸牛.巳門))
-- 蝸牛.巳門;
MetaPost 的 xpart
宏,可以獲得 pair
對象的第一個分量,另一個分量,可通過 ypart
宏獲得。
模塊
現在有關蝸牛的一切,代碼已經挺多了,全部放在繪圖代碼區域,會難以突出重點。因此,有必要將一部分需要重複使用的代碼單獨保存到一份文件,然後用 MetaPost 的 input
宏將其載入繪圖代碼區域。
例如,在 ConTeXt 源文件所在目錄新建文件 snail.mp,令其內容如下:
draw % 繪圖空間
fullsquare xscaled OverlayWidth yscaled OverlayHeight
withpen pencircle scaled 2pt
withcolor darkgreen;
def 北 expr a = (up * (a)) enddef;
def 南 expr a = (down * (a)) enddef;
def 西 expr a = (left * (a)) enddef;
def 東 expr a = (right * (a)) enddef;
def 從 = enddef;
tertiarydef a 向 b =
if pair a:
a -- (a shifted b)
elseif path a:
a -- (point (length a) of a) shifted (b)
fi
enddef;
numeric 框文配置.框.擴充, 框文配置.框.線粗, 框文配置.框.玩笑;
numeric 框文配置.框.透明度, 框文配置.背景.透明度,框文配置.文字.透明度;
color 框文配置.文字.顏色, 框文配置.框.顏色, 框文配置.背景.顏色;
path 框文配置.框形;
框文配置.框.擴充 := 4pt;
框文配置.框.線粗 := 2pt;
框文配置.框.玩笑 := 0;
框文配置.框.透明度 := 0;
框文配置.背景.透明度 := 0;
框文配置.文字.透明度 := 0;
框文配置.文字.顏色 := black;
框文配置.框.顏色 := darkgray;
框文配置.背景.顏色 := lightgray;
框文配置.框形 := 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 框文配置.背景.顏色
withtransparency (1, 1 - 框文配置.背景.透明度);
draw 框 withpen pencircle scaled 框文配置.框.線粗
withcolor 框文配置.框.顏色 withtransparency (1, 1 - 框文配置.框.透明度);
draw 文 withcolor 框文配置.文字.顏色 withtransparency (1, 1 - 框文配置.文字.透明度);)
enddef;
def 令 suffix a =
令之體(a)
enddef;
def 令之體 (suffix a) text b =
a := a shifted (b)
enddef;
def 距 expr a =
(center a)
enddef;
def 有 expr b =
shifted b
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;
加載 snail.mp 並繪製蝸牛旅行路線的完整的 ConTeXt 源文件內容如下:
\environment card-env
\startuseMPgraphic{繪圖代碼}
input snail;
picture 蝸牛;
框文配置.框.透明度 := 1;
框文配置.背景.透明度 := 1;
框文配置.框.擴充 := 5mm;
蝸牛 := 框文("巨型蝸牛");
draw 蝸牛;
名門 蝸牛;
forsuffixes i = 子門, 午門, 卯門, 酉門:
draw 蝸牛.i withpen pencircle scaled 4pt withcolor darkred;
endfor;
forsuffixes i = 醜門, 寅門, 辰門, 巳門, 未門, 申門, 戌門, 亥門:
draw 蝸牛.i withpen pencircle scaled 4pt withcolor darkgreen;
endfor;
picture 餐廳;
框文配置.框.透明度 := 0;
框文配置.框.擴充 := 4pt;
餐廳 := 框文("法國餐廳");
令 餐廳 距 蝸牛 有 (2.5cm, 0);
名門 餐廳;
draw 餐廳;
drawpathoptions(withpen pencircle scaled 2pt withcolor darkyellow);
drawarrowpath 蝸牛.卯門 -- 餐廳.酉門;
drawarrowpath
從 餐廳.午門 向 (南 1cm) 向 (西 (xpart 餐廳.午門 - xpart 蝸牛.巳門))
-- 蝸牛.巳門;
\stopuseMPgraphic
\starttext
\startstandardmakeup[align=middle]
\strut\canvas{塗鴉}\strut
\stopstandardmakeup
\stoptext
於是,世上又一個 MetaFun 模塊 snail.mp 誕生了。
結語
走需要拐彎的路徑,蝸牛不會自動駕駛。
如果需要自動駕駛……特斯拉……你需要能剎得住車。