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