Processing フォントのアウトライン情報を取得して文字をいじってみた

去年の今くらいに,輪郭追跡のアルゴリズムを使って,

文字をvertexを用いた図形にしていろいろ遊んでいました

turtley-fms.hatenablog.com

turtley-fms.hatenablog.com


 その後,

「フォントの情報を使えば,輪郭追跡しなくても文字を図形にできるのでは?」

という意見をもらっていたので,今回はそれをやってみました


結果

下に調べた情報などを書いていますが,長いのでとりあえず結果です


いろいろやってみたところ,Fontの情報ではbezierVertex()を使用しているために,

輪郭に沿って点が等間隔に配置されているわけではないことがわかりました

したがって,等間隔な点列を作成するには曲線の補間をする手間があり,輪郭追跡とあまり手間が変わらないという気持ちになりました

しかし一方で,bezierVertex()として扱えるため,

やわらかく曲線的にいじるのはやりやすそうです


とりあえずいじってみた

youtu.be

曲線的にいじってみた

youtu.be

youtu.be


githubソースコードを公開しました

displayShape()内のパラメータを変更したコードはbranchで分けています)

github.com

Fontのアウトライン情報を取得し,

beginShape()endShape()vertex()bezierVertex()を使い分ける情報と,それぞれの座標情報をクラスに持たせ,

displayShape()でそれぞれの情報をもとに図形を作成,表示しました


実装方法の調査

先駆者 その1

まずは,こちらの記事のコードをお借りしました

qiita.com

実行してみたところ,以下のような感じになりました 


(左側:shape()で表示したP, 右側:vertex()で表示したP)

f:id:turtar_fms:20181210175707p:plain
font.getShape('P', 0)
 
f:id:turtar_fms:20181210175630p:plain
font.getShape('P', 1)

なんかいまいち挙動がわからない...


右側のvertex()で表示したものを見ると,輪郭の区別も関係なく全部一つとして繋がっているために変な線が残っています


また,

font.getShape('P', 0)の時は,左側はツルツル,右側はカックカクで,

font.getShape('P', 1)の時は,左側も右側も同様に少し粗い感じのPになっています


先駆者 その2

次に,こちらの記事のコードをお借りしました

wcs.hatenablog.com

こちらではjava.awt.Fontのアウトラインを読み取って利用しています

GlyphVectorを取得して,Outlineを取得していますね


(グリフとかアウトラインとかについては,こちらの情報が参考になりました 文字とコンピュータ その3 フォントとグリフ


また,

PathIteratorcurrendSegment()typeを取得,typeの値に応じて,

beginShape() endShape() vertex() quadraticVertex() bezierVertex()

を使い分けて文字を形作っています


こちらの記事は利用できそうなのですが,

実行してみると,図形の内側の輪郭に使用するcontourがないことに気がつきました

f:id:turtar_fms:20181210200805p:plain


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)で,vertexbezierVertex()を使い分けられるか要確認(できれば楽に実装可能になる)

  • getShape('P', 1)では,文字の見た目が粗いが曲線が補間されているので使えるかも

  • ProcessingのPFont.javaの実装に謎がある

    • contourの内側にcontourが作られて良いのか?

    • 6つのパラメータを持つquadraticVertex()はP3Dで使用されるものでPFontで使うのは誤りでは?