🍣

boxes.mp の紹介

2023/08/22に公開

MetaPost のマクロパッケージ boxes.mp の紹介.

はじめに

MetaPost は TeX システムに付随するプログラムで, ソースファイルをコンパイルすることにより図(EPS, PNG, SVG)を出力する.

MetaPost 自体については, 以下のページ等を参照のこと.

boxes.mp は箱(丸)と線(矢印)による図を描くための MetaPost のマクロパッケージで, システムに標準で含まれている.

John Hobby 自身が開発の背景について語った動画もなかなか興味深い.

MetaPost についての解説記事はたくさんあるが, boxes.mp についての解説記事はあまり見かけないので, 簡単な使い方について紹介する.

具体例

boxes.mp を使うと以下のような図が比較的簡単に描ける.

例1

箱や丸と矢印の図.


fig.1 例1

オブジェクトは箱と丸(と枠なし)に対応している. 矢印はオブジェクトを定義したときに自動的に作成される結合点(c, n, sw など)を結べば簡単に描画できる. その場合, 枠線にぴったりくっつく矢印になるが, 上の例では枠線から少し離すように作図している.

fig.1.mp
beginfig(1);
u=0.5in;

% objects
boxit.f("fig.mp");
circleit.p("PNG");
boxit.b("boxes.mp");

% relative position
p.c = f.c + (3u,0);
b.c = (f.c + p.c) / 2 + (0,u);

% draw objects
drawboxed(f,p);
drawunboxed(b);

% draw arrows
z.sp = (0.1u,0.1u);
drawarrow f.e+(x.sp,0) -- p.w-(x.sp,0);
drawarrow b.s -- 0.5*(f.c+p.c)+(0,y.sp);

endfig;

例2

数学でよく出てくる可換図式.


fig.2 例2

矢印は各オブジェクトの中心を結ぶパス上で, オブジェクトの中身にかからないように描きたいが, これも比較的簡単に作図できる. 中心を結ぶパスのうち始点のオブジェクトの枠線の手前と終点のオブジェクトの枠線を超えた先をカットして矢印を描画すればよい(この機能自体は素の MetaPost のもの).

fig.2.mp
beginfig(2);
u=1cm;

def cutbfaf(suffix a,b) =
  a.c--b.c cutbefore bpath a cutafter bpath b enddef;

% objects
circleit.a(btex $A$ etex);
circleit.b(btex $B$ etex);
circleit.c(btex $C$ etex);

% relative position
b.c = a.c + (2u,0);
c.c = b.c + (0,-2u);

% draw object
drawunboxed(a,b,c);

% draw arrows
drawarrow cutbfaf(a,b);
drawarrow cutbfaf(b,c);
drawarrow cutbfaf(a,c);

% draw labels
label.top(btex $f$ etex,0.5*(a.c+b.c));
label.rt(btex $g$ etex,0.5*(b.c+c.c));
label.llft(btex $g\circ f$ etex,0.5(a.c+c.c));

endfig;

説明

boxes.mp を読み込むためにはファイルの最初に以下の文を追加する.

input boxes;

boxes.mp を読み込んだ後はだいたい以下の手順で使用することができる.

  1. オブジェクトの定義
  2. オブジェクトの位置の指定
  3. オブジェクトの描画
  4. 矢印の描画

boxes.mp の機能は1.と3.で, 2.と4.はMetaPost自体の機能を使う.

オブジェクトの定義

boxes.mp で扱う対称は箱もしくは丸であり, 最初にこれらを定義する必要がある. 使用する命令は boxit もしくは circleit である. 上の例1では以下のように定義されていた.

% objects
boxit.f("fig.mp");
circleit.p("PNG");
boxit.b("boxes.mp");

これ以降, f, p, b はオブジェクトとして参照できる.

これらの定義により, それぞれの箱と丸に以下のようなパラメータが自動的に定義される.


fig.3 boxit のパラメータ


fig.4 circleit のパラメータ

これらのパラメータは f.c(center, 中心), f.se(south-east, 南東(右下)), bpath f(boundary path, 枠線) 等のように参照することができる.

位置の指定

オブジェクトの位置を指定する. オブジェクトのパラメータに直接座標を指定してもよいが, 通常の MetaPost の使用時と同様に, 相対的な位置関係を指定する方が楽. 例えば「オブジェクトbの中心はオブジェクトaの中心から右に2u」というような指定ができる. 上の例2では以下のように指定をしている.

% relative position
b.c = a.c + (2u,0);
c.c = b.c + (0,-2u);

3つの座標を決めるためにはもう一つ条件が必要だが, 原点の位置は適当に決めてくれるようで, 上記のように3つの座標に対して, 2つの条件を指定するだけでよい.

オブジェクトの描画

オブジェクトは drawboxed() (枠線あり), もしくは drawunboxed() (枠線なし)により描画できる. 上の例1では以下のように指定している.

% draw objects
drawboxed(f,p);
drawunboxed(b);

オブジェクト f, p については枠線あり, b については枠線なしで描画している.

矢印の描画

矢印やラベルは基本的には MetaPost の機能をそのまま使用する. ただし, 矢印等の座標を指定する際にオブジェクトの結合点等のパラメータが使用できるため, 比較的簡単に矢印を描画できる.

結合点を結ぶ矢印については, 通常の MetaPost での矢印の描画と同様に始点の座標と終点の座標を与えれよい. 上の例1では以下のようになっている.

% draw arrows
z.sp = (0.1u,0.1u);
drawarrow f.e+(x.sp,0) -- p.w-(x.sp,0);
drawarrow b.s -- 0.5*(f.c+p.c)+(0,y.sp);

この例では, 基本的にはオブジェクトの結合点のパラメータを使って座標を指定しているが, 個人的な好みで z.sp だけ少し隙間を開けている.

一方, 例2では枠にある結合点は使わずに, 中心を結ぶパス上に矢印を置いている. そのような場合に cutbefore, cutafter が使用できる(上でも述べたようにこの機能は素の MetaPost のもので, boxes で実装されている機能ではない). 例2では以下の部分である.

def cutbfaf(suffix a,b) =
  a.c--b.c cutbefore bpath a cutafter bpath b enddef;

% draw arrows
drawarrow cutbfaf(a,b);
drawarrow cutbfaf(b,c);
drawarrow cutbfaf(a,c);

文が長くなるため, cutbfaf というマクロを定義して記載を簡略化している. マクロの意味は「a.cb.c を結ぶパスのうち, bpath a(オブジェクト a の枠線)の手前と bpath b(オブジェクト bの枠線)の後ろをカットする」である. この点を強調して図示すると以下のようになる.


fig.5 cutbefore/curafter

まとめ

MetaPost のマクロパッケージである boxes.mp について使い方を簡単に紹介した. boxes.mp の本質的なところはオブジェクトの定義部分にあり, それを素の MetaPost の機能(連立方程式による座標の指定, cutbefore, cutafter など)を活用して図をまとめる感じになる.

Discussion