2の機能、パスを構造ごとにまとめるという事ですが、どういう手順でまとめましょうかね。
結果的には、ここでの問題は3つあります。
1つは穴パスです。AEのテキストシェイプ機能では、塗り、いわゆる実際に描画される場所のパスと、そのパスの範囲内に穴を空けるための穴パスが存在しています。
この塗りパスと穴パスをどうやってプログラムに見分けさせるかが問題です。
2つ目は、内包パスです。アルファベットには無い構造なのですが、漢字はめんどくさいことに「回」のような、塗りパスの中の穴パスの中に更に分離した塗りパスと穴パスがあるみたいな構造が存在します。こいつを正しく分けられる必要があります。
3つ目はこの時は気付かなかったので、後で問題が表面化したタイミングで書きます。
じゃあ1つ目から解決を図ります。
シェイプレイヤーの実際のパスプロパティを見ても、何故穴と塗りが分けられているのかが分かりません。だってどう見ても同じじゃん。何のオプションも変更されてないよ。
という事で実際にパスを弄って遊んでみます。まずは完成状態から観察するわけですね。
すると、穴パスは塗りパスの範囲外から出ると、その部分が塗りパスに変わることに気付きます。
仮定1「パスの中にパスを追加すると穴パスになる」
実験してみます。シェイプレイヤーを作り、パスを引きます。そしてそのパスの中にまた別のパスを書きます。
すると、穴になりません。違うじゃん。
仮定2「パスの結合の効果によって、パスの中のパスが穴と化す」
テキストをシェイプにした時、一文字ごとにグループ化されていますが、その一文字のグループの中には必ず全てのパスの下に「パスの結合」プロパティが存在しています。複数の図形をブーリアンしたり、モチモチシェイプを作ったりするときに使うプロパティです。これは怪しいですね。
と思い、仮定1の時のシェイプにパスの結合を追加してみますが、穴は開きません。無慈悲に図形は繋がります。
「じゃあテキストのパスは一体どうなってんだこいつは」と思い、テキストのパスをいじいじしていると、ある事が判明します。
塗りパス同士が重なっても穴になりません。
穴パスが塗りパスに重ならないと穴になっていないのです。そして穴パスと穴パスが重なると塗りパスに変化します。塗りパスと穴パスは何故かそもそも性質が違うのです。プロパティに見えない違い、私がその正体を発見したのは、とあるスクリプトを使った時でした。
その名も「Create Nulls From Paths」。
AE2018から公式から提供されたシェイプのパスをヌルで制御するスクリプト、その中の「パスをトレース」が私を正解へ導きました。
今試した方はお判りでしょう。
塗りパスと穴パスでは、トレースが逆回りなのです。
頂点のインデックスの格納順が、座標空間において時計回りか、反時計回りか、これがパスの塗りと穴の違いだったのです。
多角形図形の頂点が時計回りか反時計回りかを調べる方法をググると、どうやらとある方法で面積を求めた時、それが正の値なら時計回り、負の値なら反時計回り、というアルゴリズムが存在するようです。なんか頂点の座標を足したり引いたりしてその結果が面積になる、みたいな感じでした。
てことは
for(シェイプレイヤー内の文字グループの数){
for(1文字内のパスの数){
for(パスの頂点の数){
頂点の座標をなんやかんやする。
}//ここで面積が求まる。
if(面積が正か負か){
このパスは時計回りor反時計回り
}
}
}
という感じのプログラムになるわけです。
面積の計算はこの辺のページとか見てました。
https://www.hiramine.com/programming/graphics/2d_polygonloopwise.html
そしてここで3つ目の問題が表面化します。(本当は気付いたのはもっと後
あなたは今「塗りパスって時計回りなんだ」と思いましたか?「塗りパスって反時計回りなんだ」って思いましたか?
という質問で察しがつくかと思いますが、なんとこれ、時計回りが塗りパスなのか穴パスなのかはフォントによって変わります。
「塗りパスと逆の周りで穴パスの頂点はインデックス格納されているが、どっちがどっち周りかは分からない」というのが三つ目の問題です。
2つ目の問題は骨が折れそうですし、取り合えず今判明した3つ目の問題の解決方法を考えましょう。
まず思いつくのは、塗りパスを調べて時計回りか取得する方法です。作戦1です。
AEでテキストをシェイプにした時に、繋がっていないパスはそれぞれ複数のパスデータになりますが、そのグループ内で一番上のパスは必ずいつも塗りパスなんじゃないか?と考え実験するわけです。
しかし違いました。そんなに甘い話などなかった。おのれ源柔ゴシック。何故パスの一番最初が穴パスなんだ。
というわけで、何処に塗りパスがあるかも不確定となりました。
どの位置にあろうとも必ず塗りパスとなる条件を考える必要がありそうです。
で、考え付いた作戦2がこちら
・面積が最も大きいパスは塗りパス説
結論から言えばこれが正解でした。
穴パスというのは必ず塗りパスの中に存在します。外にある穴パスは何にも穴を空けられません。また、フォント作成者がよほどエキセントリックでもない限り、塗りパスからはみ出た穴パスを使って塗るという技は文字には使われません。
という事は、穴パスによって穴を空けられる塗りパスは穴パスよりも大きくないと、穴パスを内包出来ないので、最も面積の大きいパスはかならず塗りパスになるというわけです。
パスの面積を求めるプログラムならついさっき実装した所ですとも!使っていきましょう!!
さっきのプログラムではどっち周りかを判定し、塗りか穴か判断させるif文を書いていましたが、これはもっと後に書く必要がありそうですね。ここはひとまず、ループで面積を出しながら、比較をチマチマ行って一番大きな面積を見つけ出しましょう。
for(文字数){
var pathvec = false;//trueなら塗りは時計回り、falseなら逆というフラグの変数
for(1文字内のパスの数){
var maxarea = 0;
for(パスの頂点の数){
頂点の座標をなんやかんやする。
}//ここで面積が求まる。
if(maxarea < Math.abs(今求めた面積))
maxarea = 今求めた面積
}
if(maxarea > 0)
pathvec = true;
for(1文字内のパスの数(ループ数をiとして)){
if(パス[i]の面積 > 0 == pathflag)
時計回り
else
反時計回り
}
}
}
という感じでしょうか。パスの面積は求めた時に配列変数に入れておけば、計算は一回で済みそうです。
なんとかパスが塗りか穴かは判別出来るようになったので、次の記事では2つ目の問題、内包について考えましょう。