はじめに
文字をスライスする。libtessの機能を使って実行する。これが無いと大変だろう。
元のcontoursはこんなの:
これをこんな感じで真っ二つにする:
断面は、別に直線である必要は無く、曲がっていても実行できる。要は面白いかどうかだが、それについては不問とする。技術を淡々と紹介するだけのアカウントです。よろしく。
コード全文
let font;
const {initTessy, triangulate} = fisce.foxTess;
// https://beta.p5js.org/reference/p5.font/texttocontours/
async function setup() {
createCanvas(400, 400);
font = await loadFont('https://inaridarkfox4231.github.io/assets/KosugiMaru-Regular.ttf');
textAlign(CENTER,CENTER);
textSize(280);
const prepreContours = font.textToContours('獣', width/2, height/2, { sampleFactor: 0.5 });
const preContours = [];
for(const ctr of prepreContours){
const contour = [];
for(let k=0; k<ctr.length; k++){
contour.push(ctr[k].x, ctr[k].y);
}
preContours.push(contour);
}
initTessy(libtess);
const rawContours = triangulate(preContours, {rule:"nonzero", boundaryOnly:true});
// ここでcontourを追加する
const lowerContour = [400,0,400,400,0,400];
const upperContour = [0,0,400,0,0,400];
// rawに三角を加えてabs_geq_twoを使う
const lower = triangulate([...rawContours, lowerContour], {rule:"abs_geq_two", boundaryOnly:true});
// additiveを逆向きにしてabs_geq_twoを使う
const upper = triangulate([...rawContours, upperContour], {rule:"abs_geq_two", boundaryOnly:true});
const ctx = drawingContext;
ctx.fillStyle = "white";
const drawContours = (contours) => {
ctx.beginPath();
for(const contour of contours){
for(let k=0; k<contour.length; k+=2){
if(k===0){
ctx.moveTo(contour[0], contour[1]);
}else{
ctx.lineTo(contour[k], contour[k+1]);
}
}
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
draw = () => {
background(64);
strokeWeight(4);
const t = frameCount%60;
const shift = Math.pow(t/60, 0.5)*60;
translate(2+shift,2-shift);
ctx.strokeStyle = "red";
drawContours(lower);
translate(-4-shift*2,-4+shift*2);
ctx.strokeStyle = "blue";
drawContours(upper);
}
}
p5.jsのバージョンは最新版の2.0.5です。あ、ライブラリ使ってるわこれ...まあいいか。以前紹介した方法と同じことをやってるので特に問題ないですね。ついでにもちろん1.2.2のlibtessも使ってますね。関数の内容は一緒です。
エッセンスだけ理解できればこっちを改変したバージョンで実行できるので問題ないですね。
nonzero
まずいつものようにtextToContoursでcontourを取得して、nonzeroで加工する。フォントによってはこれをしないとcontourがつながってくれないので。今回はKosugiMaruなので不要だけど。あの漢字に特に意味は無いけど漢字って穴がたくさん開いてるからテストに便利なんですよね。
追加contour/abs_geq_two
// ここでcontourを追加する
const lowerContour = [400,0,400,400,0,400];
const upperContour = [0,0,400,0,0,400];
// rawに三角を加えてabs_geq_twoを使う
const lower = triangulate([...rawContours, lowerContour], {rule:"abs_geq_two", boundaryOnly:true});
// additiveを逆向きにしてabs_geq_twoを使う
const upper = triangulate([...rawContours, upperContour], {rule:"abs_geq_two", boundaryOnly:true});
lowerとupperは右上から左下に向かう線で分かれた2つの直角二等辺三角形の外周です。これを2つ用意して、それぞれともとの文字のcontourを組み合わせます。んで、どこを取るかというと、abs_geq_twoを使います。なぜかというと結局描画領域が1なので、追加するとそれで囲まれた部分が2になるんですよね。だからabs_geq_two, 2だけ切り取られるってわけです。
描画
で、contoursが取得できたので描画します。beginContour...を使ってもいいんですけど、めんどくさいので、ctxでやります。
const drawContours = (contours) => {
ctx.beginPath();
for(const contour of contours){
for(let k=0; k<contour.length; k+=2){
if(k===0){
ctx.moveTo(contour[0], contour[1]);
}else{
ctx.lineTo(contour[k], contour[k+1]);
}
}
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
スライスされてる感を出すためにtranslateでいじってます。p5とctxがごっちゃごちゃ。
draw = () => {
background(64);
strokeWeight(4);
const t = frameCount%60;
const shift = Math.pow(t/60, 0.5)*60;
translate(2+shift,2-shift);
ctx.strokeStyle = "red";
drawContours(lower);
translate(-4-shift*2,-4+shift*2);
ctx.strokeStyle = "blue";
drawContours(upper);
}
まあ万能なライブラリなんかあるわけないので、自由にあれこれ使えばいいんです。p5しか使えないのでは窮屈なので。
最近遊んだプラットフォームが頑なにp5しか使わせてくれなかったので、ちょっと思うところがありました。自由が一番ですよね。
おわりに
ここまでお読みいただいてありがとうございました。
追記(2025/09/23)
この記事を書いてる間は全く気付かなかったんですが、この記事を見てもわかるように、「獣」の字が上の方にずれてますよね?実はtextToContoursはChrome系とFirefox系で挙動が異なるようです。ざっくりいうとFirefox系ではセンタリングに失敗します。原因は不明です。次の更新で直されるでしょう。

