美文网首页
MetaFun 04:静物

MetaFun 04:静物

作者: 明夷 | 来源:发表于2021-05-15 09:21 被阅读0次

蜗牛生活的世界,有许多静物,其中据说有法国的餐厅。蜗牛需要爬行,这些静物同样需要。所有的静物,可以从同一个地方爬出来。在为静物爬行编造故事之前,要对它们有所介绍。

文字

第一个静物是文字,确切地说,是 TeX 世界里的文字。

文字可使用 textext 宏构建,例如

vardef text (expr s) =
  if picture s:
    s
  else:
    fullsquare scaled 1cm withcolor red
  fi
enddef;

draw text(textext("来自 \TEX\ 世界的文本"));

在 MetaPost 语言里,vardef 宏用于定义有返回值的宏。上述代码主要目的是为了展现,textext 构造的对象,其类型是 picture

结果为

带框的文字

为文字加一个外框,在 MetaPost 里要比在 ConTeXt 里容易得多,只需先绘制一个外框,再绘制文字即可,例如:

draw 
  fullsquare xscaled 2cm yscaled 1cm 
  withpen pencircle scaled 4pt withcolor darkgrey;
draw textext("来自 \TEX\ 世界的文字") withcolor darkred;

结果为

然而,这样的结果是出于巧合—— fullsquare 构造的封闭路径和 textext 构造的 picture 对象,它们的中心点默认情况下恰好皆为原点。

貌合神离,不可接受。最好的解决方案是将它们组合为一个 picture 对象,例如

picture framed_text;
framed_text := image(
  draw 
    fullsquare xscaled 2cm yscaled 1cm 
    withpen pencircle scaled 4pt withcolor darkgrey;
  draw textext("来自 \TEX\ 世界的文字") withcolor darkred;
);

draw framed_text;

将文字和外框合成为 picture 对象的好处是,可以对它们进行整体性的旋转和平移变换。例如,

draw framed_text;
draw framed_text shifted (-2cm, -1cm);
draw framed_text rotated 30;
draw framed_text rotated 75 shifted (2cm, 1cm);

结果为

包围盒

为不同的文字制作外框,需要人为设定外框的尺寸,令其能够包含文本,这种事情甚为无趣。

MetaFun 提供了两个宏,bbwidthbbheight,它们可分别用于计算 pathpicture 对象的包围盒宽度和高度,例如

picture text;
text := textext("来自 \TEX\ 世界的文字");
path frame;
frame := fullsquare xscaled (bbwidth text) yscaled (bbheight text);

draw frame withpen pencircle scaled 2pt withcolor darkgray;
draw text withcolor darkred;

结果为

文字的外框过于紧致,可使用 MetaFun 的 enlarged 宏令其有所扩大:

path frame;
frame := fullsquare xscaled (bbwidth text) yscaled (bbheight text);
frame := frame enlarged (4pt, 4pt);

上述代码里,enlargedframe 的四周向外延伸 4pt 的距离。结果如下

于是,一个能与自身包含的文字区域如影随形的外框就有办法定义了。

框文

带框的文字,我原本是想表达为 framed text,但是忽然觉得,叫框文也不错:

vardef 框文 expr a =
  path 框; picture 文;
  文 = textext(a);
  框 := fullsquare xscaled (bbwidth 文) yscaled (bbheight 文)
        enlarged (4pt, 4pt);
  image(draw 框 withpen pencircle scaled 2pt withcolor darkgray;
        draw 文 withcolor darkred;)
enddef;

draw 框文("来自 \TEX\ 世界的文字");

局部变量

框文 宏,很前卫,但是它的定义里的变量,都是全局变量,即使它们在宏内。下面这个实验能够给出证明:

draw 框文("来自 \TEX\ 世界的文字");
draw 文 rotated 30 shifted (1cm, 1cm);

结果为

迄今为止,我定义的所有变量皆为全局变量。过多使用全局变量,程序缺乏发展壮大的可能。

MetaPost 支持局部变量,只是要多写一些代码:

vardef 框文 expr a =
  begingroup
  save 框, 文;
  path 框; picture 文;
  文 = textext(a);
  框 := fullsquare xscaled (bbwidth 文) yscaled (bbheight 文)
        enlarged (4pt, 4pt);
  image(draw 框 withpen pencircle scaled 2pt withcolor darkgray;
        draw 文 withcolor darkred;)
  endgroup
enddef;

重新定义的 框文,其中的变量在外部便无法使用了。

对于 vardef 定义的宏,它内含了 begingroupendgroup,因此上述代码可写为

vardef 框文 expr a =
  save 框, 文;
  path 框; picture 文;
  文 = textext(a);
  框 := fullsquare xscaled (bbwidth 文) yscaled (bbheight 文)
        enlarged (4pt, 4pt);
  image(draw 框 withpen pencircle scaled 2pt withcolor darkgray;
        draw 文 withcolor darkred;)
enddef;

背景色

现在,对 框文 再增加一个小功能,为框增加背景色:

vardef 框文 expr a =
  save 框, 文;
  path 框; picture 文;
  文 = textext(a);
  框 := fullsquare xscaled (bbwidth 文) yscaled (bbheight 文)
        enlarged (4pt, 4pt);
  image(draw 框 withpen pencircle scaled 2pt withcolor darkgray;
        fill 框 withcolor lightgray;
        draw 文 withcolor darkred;)
enddef;

效果如下图所示

外观

有什么理由决定,框文的框是暗灰色的,框的背景是浅灰色的,文字是暗红色的,以及有什么理由决定,框要向四周延伸 4pt 范围,框的粗细为 2pt?

没有理由。这些在定义一个宏时,都是不可确定的因素,要在具体场景里调用宏的时候方能确定。

在定义宏时,有两种办法对付这些不可确定的因素,一种是用宏的参数,一种是用全局变量。如果不确定的因素太多,宏就需要很多参数,其中有些参不确定因素在多数情况下也可以用默认的值。出于这一考虑,我觉得,用全局变量控制 框文 里的不确定因素,更为方便。但是,需要试验,看是否可行。

首先,试着将 enlarged 的参数定义为全局变量:

numeric 扩充;
扩充 := 4pt;

然后在 框文 的定义里使用 padding

vardef 框文 expr a =
  save 框, 文;
  path 框; picture 文;
  文 = textext(a);
  框 := fullsquare xscaled (bbwidth 文) yscaled (bbheight 文)
        enlarged (扩充, 扩充);
  image(draw 框 withpen pencircle scaled 2pt withcolor darkgray;
        fill 框 withcolor lightgray;
        draw 文 withcolor darkred;)
enddef;

然后在调用 框文 时,可以修改 padding 的值,从而达到控制框文外观的目的,例如:

扩充 := 1cm;
draw 框文("来自 \TEX\ 世界的文字");

结果为

既然如此成功,那么就为 框文 定义所有的全局变量:

numeric 扩充, 线粗, 玩笑;
扩充 := 4pt; 线粗 := 4pt; 玩笑 := 0;
color 文色, 框色, 背景;
文色 := black; 框色 := darkgray; 背景 := lightgray;

然后将 框文 的定义修改为:

vardef 框文 expr a =
  save 框, 文;
  path 框; picture 文;
  文 = textext(a);
  框 := fullsquare xscaled (bbwidth 文) yscaled (bbheight 文) enlarged (扩充, 扩充);
  if 玩笑 > 0: 框 := 框 randomized 玩笑; fi;
  image(draw 框 withpen pencircle scaled 线粗 withcolor 框色;
        fill 框 withcolor 背景;
        draw 文 withcolor 文色;)
enddef;

那个叫玩笑的变量,轻易不要用,用了就不严肃了:

文色 := darkred;
扩充 := 5mm;
玩笑 := 扩充;
draw 框文("来自 \TEX\ 世界的文字");

姓甚名谁

利用 MetaPost 对变量名称的限定非常之少的特性,可以为全局变量建立命名空间,以防名字这种非常宝贵的资源被很快耗尽。

我可以将 框文 宏依赖的所有全局变量定义为

numeric 框文.框.扩充, 框文.框.线粗, 框文.框.玩笑;
框文.框.扩充 := 4pt; 
框文.框.线粗 := 4pt; 
框文.框.玩笑 := 0;

color 框文.文字.颜色, 框文.框.颜色, 框文.框.背景;
框文.文字.颜色 := black; 
框文.框.颜色 := darkgray; 
框文.框.背景 := lightgray;

形如 框文.框.扩充 之类的变量名,不妨读作「框文的框的扩充」。虽然上述代码有些繁琐,但是安全。虽然安全,但是却不可行。因为全局变量名称里的 框文框文 这个宏名存在冲突。我言不虚,名字的确是稀有资源。既然如此,就让全局变量的名字再冗长一些:

numeric 框文配置.框.扩充, 框文配置.框.线粗, 框文配置.框.玩笑;
框文配置.框.扩充 := 4pt; 
框文配置.框.线粗 := 4pt; 
框文配置.框.玩笑 := 0;

color 框文配置.文字.颜色, 框文配置.框.颜色, 框文配置.框.背景;
框文配置.文字.颜色 := black; 
框文配置.框.颜色 := darkgray; 
框文配置.框.背景 := lightgray;

然后再对 框文 的定义适应性修改:

vardef 框文 expr a =
  save 框, 文;
  path 框; picture 文;
  文 = textext(a);
  框 :=
    fullsquare
    xscaled (bbwidth 文) yscaled (bbheight 文)
    enlarged (框文配置.框.扩充 * (1, 1));
  if 框文配置.框.玩笑 > 0: 框 := 框 randomized 框文配置.框.玩笑; fi;
  image(draw 框 withpen pencircle scaled 框文配置.框.线粗 withcolor 框文配置.框.颜色;
        fill 框 withcolor 框文配置.框.背景;
        draw 文 withcolor 框文配置.文字.颜色;)
enddef;

框形

有什么理由可以确定,框文的框一定是矩形或正方形呢?

没有。

所以,还应该再增加一个全局变量,

path 框文配置.框形;
框文配置.框形 := fullsquare;

但是,现在到了重新理解 enlarged 宏的用法的时候了。enlarged 并非是对一个路径对象进行扩张或缩小,它左边的参数可以是任意路径,右边是扩张或缩小的程度,但是它的返回值是一个矩形。所以,

即使将框形设为圆,

框文配置.框形 := fullcircle;
draw 框文("来自 \TEX\ 世界的文字");

得到的结果依然是矩形的框文。所以,需要继续修改 框文 的定义:

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(draw 框 withpen pencircle scaled 框文配置.框.线粗 withcolor 框文配置.框.颜色;
        fill 框 withcolor 框文配置.框.背景;
        draw 文 withcolor 框文配置.文字.颜色;)
enddef;

现在,

框文配置.框形 := fullcircle;
框文配置.框.扩充 := 5mm;
框文配置.文字.颜色 := darkred;
draw 框文("来自 \TEX\ 世界的文字");

结果就有了一个椭圆形状的框文:

fullsquarefullcircle 绘制的图形,皆为单位图形,即它们的包围盒长度和宽度皆为 1。在 MetaPost 语言里,不带任何长度单位的数字,例如 1,它们也是有长度的,它们使用的长度单位是 bp(big point)。打印机知道 1bp 的尺度是多大。

结语

蜗牛很失望,没有遇到一个叫作法国餐馆的静物,导致它无从展现自己的逃跑速度。

相关文章

  • MetaFun 04:静物

    蜗牛生活的世界,有许多静物,其中据说有法国的餐厅。蜗牛需要爬行,这些静物同样需要。所有的静物,可以从同一个地方爬出...

  • 读沈大成的《知道宇宙奥义的人》(3)

    2023-01-04 五、静物油画般的写景 沈大成作品对景物的描写有一种静物油画的感觉,换句话说,就是冷冰冰的,没...

  • MetaFun 09:进度

    蜗牛的身影消失在长亭外,去寻它的山和海了。我还在城里的宫廷间徘徊。 许多天后,我的小手指不再那么木然隐痛了,于是最...

  • MetaFun 06:门

    每个静物可以有很多门。蜗牛可以从一扇门进去,也可以从另一扇门出来。 有些门有名字,有些门没名字。 有名字的门,简称...

  • MetaFun 01:涂鸦

    有了画布,就可以涂鸦。画布是代码,涂的也就是代码,确切地说,是 MetaPost 代码,然而更确切地说,是 Con...

  • MetaFun 03:蜗牛

    鸦在天上飞,失去了踪影。 地上有一只蜗牛,慢吞吞地爬,留下没有规律的痕迹,在阳光下略有些闪亮,闪亮里有些怀旧,在夏...

  • 素描静物组合04

    工具:1.温莎牛顿素描纸; 2.施德楼蓝杆铅笔。

  • MetaFun 02:隐者鸦

    一幅画里的一只即将被涂的鸦,该如何完美地隐藏自己的踪迹呢? 庄子说,你知道地籁,知道人籁,但是你知道天籁吗? 隐者...

  • MetaFun 07:Lua 来了!

    蜗牛是简单的,只需要爬来爬去。静物是复杂的,即使它一生只爬一次。 框文配置 不知不觉,我已经为频频用于表示静物的框...

  • MetaFun 08:长亭外

    长亭外,古道边……现在开始回忆。 起初,在 card-env.tex 里,我定义了一个叫作 foo 的 overl...

网友评论

      本文标题:MetaFun 04:静物

      本文链接:https://www.haomeiwen.com/subject/ntsxjltx.html