Processing フォントのアウトライン情報を取得して文字をいじってみた
去年の今くらいに,輪郭追跡のアルゴリズムを使って,
文字をvertexを用いた図形にしていろいろ遊んでいました
その後,
「フォントの情報を使えば,輪郭追跡しなくても文字を図形にできるのでは?」
という意見をもらっていたので,今回はそれをやってみました
結果
下に調べた情報などを書いていますが,長いのでとりあえず結果です
いろいろやってみたところ,Fontの情報ではbezierVertex()を使用しているために,
輪郭に沿って点が等間隔に配置されているわけではないことがわかりました
したがって,等間隔な点列を作成するには曲線の補間をする手間があり,輪郭追跡とあまり手間が変わらないという気持ちになりました
しかし一方で,bezierVertex()として扱えるため,
やわらかく曲線的にいじるのはやりやすそうです
とりあえずいじってみた
曲線的にいじってみた
(displayShape()内のパラメータを変更したコードはbranchで分けています)
Fontのアウトライン情報を取得し,
beginShape()やendShape(),vertex()やbezierVertex()を使い分ける情報と,それぞれの座標情報をクラスに持たせ,
displayShape()でそれぞれの情報をもとに図形を作成,表示しました
実装方法の調査
先駆者 その1
まずは,こちらの記事のコードをお借りしました
実行してみたところ,以下のような感じになりました
(左側:shape()で表示したP, 右側:vertex()で表示したP)


なんかいまいち挙動がわからない...
右側のvertex()で表示したものを見ると,輪郭の区別も関係なく全部一つとして繋がっているために変な線が残っています
また,
font.getShape('P', 0)の時は,左側はツルツル,右側はカックカクで,
font.getShape('P', 1)の時は,左側も右側も同様に少し粗い感じのPになっています
先駆者 その2
次に,こちらの記事のコードをお借りしました
こちらではjava.awt.Fontのアウトラインを読み取って利用しています
GlyphVectorを取得して,Outlineを取得していますね
(グリフとかアウトラインとかについては,こちらの情報が参考になりました
文字とコンピュータ その3 フォントとグリフ)
また,
PathIteratorのcurrendSegment()でtypeを取得,typeの値に応じて,
beginShape() endShape() vertex() quadraticVertex() bezierVertex()
を使い分けて文字を形作っています
こちらの記事は利用できそうなのですが,
実行してみると,図形の内側の輪郭に使用するcontourがないことに気がつきました

PFontのソースコード
processing/PFont.java at master · processing/processing · GitHub
ProcessingのPFontのソースコードを読んでみると以下のように書かれていました
public PShape getShape(char ch, float detail) { ... // make everything into moveto and lineto PathIterator iter = (detail == 0) ? shp.getPathIterator(null) : // maintain curves shp.getPathIterator(null, detail); // convert to line segments ... }
「detailが0のときはカーブのまま,0でないときはラインでの分割に変換する」
と書いてありますね
どうやら,ツルツルした文字のまま使いたいならカーブを使わないといけないみたいです
また,その下には次のように書かれています
int contours = 0; ... while (!iter.isDone()) { int type = iter.currentSegment(iterPoints); switch (type) { case PathIterator.SEG_MOVETO: // 1 point (2 vars) in textPoints ... if (contours == 0) { s.beginShape(); } else { s.beginContour(); } contours++; s.vertex(iterPoints[0], iterPoints[1]); break; case PathIterator.SEG_LINETO: // 1 point ... s.vertex(iterPoints[0], iterPoints[1]); break; case PathIterator.SEG_QUADTO: // 2 points ... s.quadraticVertex(iterPoints[0], iterPoints[1], iterPoints[2], iterPoints[3]); break; case PathIterator.SEG_CUBICTO: // 3 points ... s.quadraticVertex(iterPoints[0], iterPoints[1], iterPoints[2], iterPoints[3], iterPoints[4], iterPoints[5]); break; case PathIterator.SEG_CLOSE: ... if (contours > 1) { ... s.endContour(); } break; } ... iter.next(); } s.endShape(CLOSE); return s; }
先駆者 その2の方のコードに似ていますね
こちらのコードではcontourについての実装がされています
ただ,よく読むとcotoursが0以上では,beginContour()を実行するようになっていますが,
contoursはその後インクリメントのみ実行され,初期化はされません
したがって,以下のような図形を描画するコードになる可能性があります
beginShape(); beginContour(); endContour(); endShape(); beginContour(); beginContour(); endContour(); endContour();
すなわち,contourの内側にcontourが作られる可能性があります
Processingの実装上はこれでいいのですかね??謎です
ちなみにこのコード,もう一つ謎な点があります
case PathIterator.SEG_CUBICTOでは,6つパラメータを持つquadraticVertex()が使用されています
これ,P3Dで使用するときの使い方なはずなんですよね
quadraticVertex() \ Language (API) \ Processing 3+
おそらく,bezierVertex()が正しいと思うのですがなぜこうなっているのか...
(先駆者 その2の方の記事ではbezierVertex()になっています)
調査のまとめ
PFontの
getShape('P', 0)で,vertexとbezierVertex()を使い分けられるか要確認(できれば楽に実装可能になる)getShape('P', 1)では,文字の見た目が粗いが曲線が補間されているので使えるかもProcessingのPFont.javaの実装に謎がある
contourの内側にcontourが作られて良いのか?
6つのパラメータを持つ
quadraticVertex()はP3Dで使用されるものでPFontで使うのは誤りでは?