MetaFun 06:門 名門 隱框 歷險 減肥 模塊 結語

每個靜物可以有很多門。蝸牛可以從一扇門進去,也可以從另一扇門出來。

有些門有名字,有些門沒名字。

有名字的門,簡稱名門,有 12 個,分別叫子、醜、寅、卯、辰、巳、午、未、申、酉、戌、亥。子門在正北,卯門在正東,午門在正南,酉門在正西。

沒名字的門,可以根據有名字的門構建。

名門

假設有一靜物 foo

picture foo;
框文配置.框.擴充 := 5mm;
foo := 框文("名門");
draw foo;

它的子門在哪呢?

ulcorner foourcorner foo 的正中,即 foo 的包圍盒的左上角頂點和右上角頂點的中點,用 MetaPost 語言可表示爲

pair foo.子門;
foo.子門 := .5[ulcorner foo, urcorner foo];

不妨驗證一下:

draw foo.子門 withpen pencircle scaled 4pt withcolor darkred;

結果爲

同理,foo.午門llcorner foolrcorner 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 的 drawpathdrawarrowpath 宏默認使用的筆和顏色,可通過 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 誕生了。

結語

走需要拐彎的路徑,蝸牛不會自動駕駛。

如果需要自動駕駛……特斯拉……你需要能剎得住車。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章