- JavaScript のブロック体アルファベット生成器から出発し、2か月後には 筆記体の手書き文字までコードで実装した過程
- 文字は p5js ツールで 重要な座標を打ち、Chaikin 曲線で整えた後、可変太さのパスに変換される
- 筆記体は文字ペアごとに接続方法が異なるため、パスの開始・終了に 0〜3 のタグを付け、接続不可・ベースライン・ベースライン上・x-height 付近の状態を区別する
- 単語を作る際は、各文字の 2〜3 個のパスから一つを選び、隣接文字の接続高さに応じて点を追加・削除・置換した後、Perlin noiseで揺らぎを加える
- 縮小後の筆記体文字クラスは 26.1kbで、ブロック体の 9.7kb より大きく、複数のパスと隣接文字補正関数が容量増加の原因
ブロック体生成方式から筆記体への拡張
- 以前のブロック体アルファベット生成器は、各文字パスの 重要な点をコードで定義する方式だった
- 文字ごとに約 10 個の点を配置する
- Chaikin 曲線アルゴリズムでパスを滑らかにする
- パスを長さ方向に太さが変わる形に変換する
- p5js で最終的な形を描画する
- ブロック体では座標をコードに直接書き、文字が自然になるまで点を手動で調整する必要があった
- 筆記体の実装では、この反復作業を減らすために 文字設計ツールを別途作成した
p5js で文字パスを設計するツール
- p5js エディタで 重要な点を定義して出力するツールを作成して使用した
- ツールは、スケールと文脈を示すサンプル文字と、新しい文字を設計する領域をあわせて提供する
- クリックでパスの重要な点を配置すると、Chaikin 曲線が適用された結果が表示される
p キーで編集モードに切り替える
- 点を選択してドラッグし、位置を調整する
enter キーでパスをコンソールに出力する
- 各文字ごとに 2〜3 個のオプションを作成し、結果のパスは
{x, y} 座標配列の形になる
- 自分の手書き文字を基準にするため、小文字と大文字の例を自分で書き、その画像を文字生成ツールに読み込んでなぞった
w/a/s/d キーで画像を移動する
r/e キーで画像を拡大・縮小する
- 紙に書いた数字は、その領域を文字生成ウィンドウに合わせるための x/y 座標
筆記体の接続問題と 0〜3 タグ
- ある文字ペアは、1 文字の重要点パスの後ろに次の文字パスをつなげ、まとめて Chaikin 曲線を適用するだけで自然につながる
- しかし一部の組み合わせでは、接続線が文字の形を崩してしまう
na では n の最後の点が低く、a の最初の点が高いため、接続線が a を斜めに横切り、e のように見えることがある
ti では t がベースラインのすぐ上で終わり、i がベースラインから始まるため、不自然な尾根が生じる
- 特定の文字ペアごとにルールを書く代わりに、各パスの開始と終了に 接続高さ情報を数字で付ける
0: 他の文字と接続できない
1: ベースライン付近で他の文字と接続する
2: ベースラインのすぐ上で他の文字と接続する
3: x-height 付近で他の文字と接続する
- 同じ文字でも、単語の開始位置や前後の文字によって点の追加・削除方法が変わる
a が単語の先頭にあると、追加点が不自然になる
w の後の a では、別の形で線が a を横切ることがある
t が k とつながると、形が変形することがある
- すべての文字ペアをテストし、複数のパスオプションと隣接文字ごとの編集によって 変化の幅を作った
- 理想的には文字ごとに 5〜6 個のパスオプションを用意したいが、ファイルサイズとのバランスを取る必要がある
単語生成手順
- 単語を作るときは、まず各文字について 2〜3 個の基本パスのうち一つを選ぶ
- 選ばれたパスの開始・終了接続情報が隣接文字に渡される
- 同じ文字でも選ばれたパスオプションによって終点情報が異なる場合があるため、すべての文字パスを先に選ぶ必要がある
- その後、基本パスは隣接文字に合わせて調整される
- 前の文字の終了高さが
2 なら、現在のパスの開始点を一つ削除できる
- 次の文字の開始高さが
1 なら、特定の位置に点を追加できる
- 調整関数は文字ごとに異なる動作をする
q の調整関数は 70% の確率で末尾に切れ目を追加し、前の文字の終了情報や次の文字の開始情報に応じて点を置換または追加する
n の調整関数は、次の文字が 3 から始まるとき、30% の確率で切れ目を作るか最後の点を移動する
- すべての文字の基本パスを結合するとき、
1、2、3 は無視し、0 が出たら新しいパスを開始して 切れ目を作る
- 結合されたパスには Chaikin 曲線を適用し、可変太さの形に変換した後、Perlin noiseでわずかな揺らぎを加える
容量と活用方法
- ブロック体用の文字クラスは 9.7kbで、縮小処理した筆記体文字クラスは現在 26.1kb
- 筆記体のほうが大きい理由は、文字ごとに複数のパスがあり、隣接文字に合わせて点を調整する関数が入っているため
- 追加の削減アイデアも残っている
- 現在の文字は基本フォントサイズ 20 を基準に設計した後、リサイズしている
- そのため
x: 14.5 のような座標が多い
- 基本サイズを 200 に変えれば、
145 のように小数点をなくして 1 文字分を減らせる
- この変更は慎重に行う必要があるため、今後の TODO に入っている
- この手書き文字の主な用途は、作業中の ダイアグラムのタイトル、ラベル、落書きメモ
- フォントの代わりにエンコードされたパスを使うと、文字の位置や個々の文字内部の太さを直接変えるといった形で パスを操作できる
- この手書き文字コードの発展版を使った
Meaningful Nonsense プリントも提供されており、各プリントは注文時に生成される固有の作品
1件のコメント
Hacker News のコメント
自分の手書き文字をエンコードしたら、一方向ハッシュと見なしてよさそう
後で読み返しても、いったい何を書こうとしていたのか解読する方法がない
Stuff Made Here の、似た内容を扱った YouTube 動画がかなりよい: https://www.youtube.com/watch?v=cQO2XTP7QDw
試行錯誤も説明してくれていて見ていて面白かったし、手書き文字合成のリポジトリも興味深い: https://github.com/sjvasquez/handwriting-synthesis
最後のアートワークがかなり美しい
次の段階は、これを実際のフォントにして、どんなプログラムでも入力できるようにすることなのか気になる
私たちが普通考えるフォントとは違って、塗りつぶす輪郭ではなく、なぞって描く経路でなければならない
ただし手作業でやる必要があったり、そうしたかったりするなら退屈かもしれない
文字のつながりが合わないという部分はよく分からない
自分にとって筆記体の核心はペンを紙から離さないことで、だから文字をつなぐ方法はそもそも書く行為の中に含まれている
作者は実際にはそう書いていないので問題が起きたように見える
それでも、フォントをいじらずに画面に複数の文字を描く最も単純な方法として解決している残りの文章はとても面白い
筆記体での文字間の接続は文脈依存だ
たとえば
bの次にaが来るかoが来るかによって、可読性のために形が変わることがあるし、単語の中でも場合によっては文字の間でペンを離したくなることがあるそれでも筆記体の「規則」を破っているわけではない
違うように習ったのかもしれないし、その教えが正しいのかもしれないが、接続が文脈依存であってはならない筆記体の流儀はあまり知らない
つながらないフォントでもカーニングに似て、複数の文字列がどう見えるかをエンコードする必要がある
美しい! 筆記体の手書きフォントがもっと増えてほしい
2年前に作った自分の作品はここにある: https://certik.github.io/slabikar-otf/
すごいけれど、自分の字が自分専用フォントにしたいと思えるくらい良かったらよかったのに :)
< 14.5の代わりにデフォルトサイズを 200 に変えれば、点を 145 と定義できるので、小数点文字を1つ減らせるadjustという関数が見えるが、フォント仕様はよく分からないものの、0,{x:12.2,y:13.2}を0,[12.2,13.2]のように別の形でシリアライズして、adjust関数で変換するのはどうだろうと思う