SATySFi で \widehat を実装する
これは SATySFi Advent Calender 2020 の 9 日目の記事です. 8 日目は amutake さんによる「SATySFi で書かれた本などのまとめを作りました」でした. 10 日目は monaqa さんによる「【入門記事】SATySFi のコマンドの文法」です.
\widehat
今回は, SATySFi の数式コマンドとして \widehat を実装したいと思います.
TeX の \widehat と同じように,数式内で
${ \widehat{x}, \widehat{x^2}, \widehat{xy}, \widehat{xyz} }
の形で使えるようにします.見た目は次のようになります:
型は次のようになります.
\widehat: math → math
使用するプリミティブ
今回使用するプリミティブを順に説明していきます.
inline-graphics
inline-graphics: length → length → length → (point → graphics list) → inline-boxes
グラフィックは graphics 型によって表されるのですが,実際にこれを inline-boxes として本文中に組み込むためには,まず横幅/高さ/深さを教えることで SATySFi に組版して文章中の位置を決めてもらい,そうして決定された左下の座標を基準として graphics を作る必要があります.これを実現するのが inline-graphics プリミティブです.
第 1, 2, 3 引数にそれぞれ横幅,高さ,深さを渡し,第 4 引数には「座標を受け取って graphics を返す関数」を渡します.
これが inline-boxes となって数式中に入るので, graphics list には \widehat 自体に加えて, \widehat を付ける対象となる数式も含める必要があります.
embed-math
embed-math : context → math → inline-boxes
\widehat を付ける対象となる数式を,まずこれで inline-boxes に変換します.すると, get-natural-metrics によって横幅/高さ/深さの情報が得られるようになり,また draw-text によって graphics に変換することができるようになります.
get-natural-metrics
get-natural-metrics: inline-boxes → length * length * length
inline-boxes の横幅/高さ/深さを得ます.これをもとに, \widehat の位置や大きさと, inline-graphics に渡す最終的な横幅/高さ/深さを決めます.
draw-text
draw-text: point → inline-boxes → graphics
左下の座標と inline-boxes を渡すと,そこに inline-boxes の内容を描いた結果の graphics を返します.
start-path, line-to, close-with-line
start-path: point → pre-path
line-to: point → pre-path → pre-path
close-with-line: pre-path → path
graphics を描くためには,まず path を作ります. |> 演算子を用いてメソッドチェーンのように書くと例えば次のようになります:
% 左下の座標が (x, y) で一辺の長さが 1pt の正方形を表す path
start-path (x, y)
|> line-to (x, y +' 1pt)
|> line-to (x +' 1pt, y +' 1pt)
|> line-to (x +' 1pt, y)
|> close-with-line
fill
fill: color → path → graphics
path の内側を color で塗った graphics を返します.
text-in-math
text-in-math: math-class → (context → inline-boxes) → math
完成した inline-boxes を,数式中に組み込みます. \widehat の付いた数式の math-class はMathOrd で良いでしょう.
設計
\widehat は,真ん中の辺りで厚く,端の辺りで薄くなってほしいです.それぞれの厚さを hat-thickness-mid , hat-thickness-end とします.
\widehat 自体の高さ hat-height は,横幅に比例させます.
また, \widehat は,数式より少し高い場所に置かれるので,その間隔を hat-pos とします.数式の高さより hat-pos だけ上の位置から \widehat を書き始める感じです.
それぞれの具体的な値は次のようにしてみました.
| 変数 | 値 |
|---|---|
hat-thickness-mid |
.6pt |
hat-thickness-end |
.2pt |
hat-height |
横幅の .2 倍 |
hat-pos |
1.2pt |
できたもの
最終的にこんな感じになりました:
let-math \widehat it =
text-in-math
MathOrd
(fun ctx -> (
let it = embed-math ctx ${#it} in
let (length, height, depth) = get-natural-metrics it in
let hat-thickness-mid = .6pt in
let hat-thickness-end = .2pt in
let hat-height = length *' .2 in
let hat-pos = 1.2pt in
inline-graphics
length
(height +' hat-pos +' hat-height +' hat-thickness-mid)
depth
(fun (x, y) -> [
draw-text (x, y) it;
fill
Color.black
(
start-path (x, y +' height +' hat-pos)
|> line-to (x, y +' height +' hat-pos +' hat-thickness-end)
|> line-to (x +' length *' 0.5, y +' height +' hat-pos +' hat-height +' hat-thickness-mid)
|> line-to (x +' length, y +' height +' hat-pos +' hat-thickness-end)
|> line-to (x +' length, y +' height +' hat-pos)
|> line-to (x +' length *' 0.5, y +' height +' hat-pos +' hat-height)
|> close-with-line
)
])
))
良ければ使ってみてください!
Discussion