0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

libtessを使ってテキストを切り刻んでばらばらにする

Posted at

はじめに

 libtessの応用として、与えられた図形をバラバラにしてみようと思います。

コード全文

slice falling

依存ライブラリ

    <script src="https://cdn.jsdelivr.net/npm/p5@2.0.5/lib/p5.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/libtess@1.2.2/libtess.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/opentype.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/fisce.js@1.1.6/src/index.min.js"></script>

メインコード

const {ResourceLoader} = fisce.foxUtils;
const {getTextContours} = fisce.foxApplications;

const {initTessy, triangulate} = fisce.foxTess;

/*
  手順
  textToContoursでとりあえず取得しちゃう
  libtess用に改変
  tessyを初期化
  triangulateのnonzeroでcontour結合
  再びベクトル配列に戻す
  おわり。
  fisceは1.1.6になりました

  textToContoursはブラウザ依存で不便なので自作ライブラリのgetTextContoursを使うことにしました
  p5は1.11.10にしました。vectorのrotateがバグってるようです。2.0.5は使いません。

  原因わかったのでとりあえず2.0.5で書きます。
*/

async function setup() {
  createCanvas(400, 400);

  const ab = await ResourceLoader.getArrayBuffer("https://inaridarkfox4231.github.io/assets/KosugiMaru-Regular.ttf");
  const font = await opentype.parse(ab);
  const prepreContours = getTextContours({
    targetText:"", textScale:280, font:font, position:{x:width/2, y:height/2}, alignV:"center", alignH:"center"
  });

  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});

  const ctx = drawingContext;
  ctx.fillStyle = "gray";

  const contours1 = sliceContours(rawContours, 0, PI/3);
  const contours2 = sliceContours(contours1, 10, -PI/5);
  const contours3 = sliceContours(contours2, 10, -PI/3-0.08);
  const contours4 = sliceContours(contours3, 5, PI/2);
  const contours5 = sliceContours(contours4, 5, PI*7/6);
  const contours6 = sliceContours(contours5, 50, PI*3/4);
  const contours7 = sliceContours(contours6, 50, PI*5/4);
  const contours8 = sliceContours(contours7, 50, PI*7/4);
  const contours9 = sliceContours(contours8, 0, 0);
  const contours10 = sliceContours(contours9, 25, PI*3/4);
  const contours11 = sliceContours(contours10, 50, PI/4);
  const contours12 = sliceContours(contours11, 50, PI*3/2);
  const contours13 = sliceContours(contours12, 80, -PI/4);
  //drawContours(contours3, ctx);

  const shapes = [];

  const allContours = [rawContours, contours1, contours2, contours3, contours4, contours5, contours6, contours7, contours8, contours9, contours10, contours11, contours12, contours13];
  let _id=0;
  draw = () => {
    background(205);
    if(_id<14){
      drawContours(allContours[_id], ctx);
    }else{
      for(const h of shapes){
        h.update();
        h.display(ctx);
      }
      for(let k=shapes.length-1; k>=0; k--){
        if(!shapes[k].alive){
          shapes.splice(k,1);
        }
      }
      if(shapes.length===0){
        console.log("complete!");
        noLoop();
      }
    }
    if(frameCount%8===0){_id++;}
    if(_id === 14){
      for(const ctr of contours13){
        shapes.push(new Shape(ctr));
      }
      _id++;
    }
  }
}

function shiftContours(contours, a, b){
  for(const contour of contours){
    for(let i=0; i<contour.length; i+=2){
      contour[i] += a;
      contour[i+1] += b;
    }
  }
}

// libtess用
function drawContours(contours, ctx){
  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.lineTo(contour[0], contour[1]);
  }
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
}

// p5.Vector用
function drawContoursForVector(contours, ctx){
  ctx.beginPath();
  for(const contour of contours){
    for(let k=0; k<contour.length; k++){
      if(k===0){
        ctx.moveTo(contour[0].x, contour[0].y);
      }else{
        ctx.lineTo(contour[k].x, contour[k].y);
      }
    }
    ctx.lineTo(contour[0].x, contour[0].y);
  }
  ctx.closePath();
  ctx.fill();
  ctx.stroke();
}

function sliceContours(contours, r, t){
  const L = Math.max(width,height)*4;
  let corners = [createVector(-L,-L),createVector(L,-L),createVector(L,L),createVector(-L,L)];
  corners = corners.map((v) => {return v.add(r,-r);});
  corners = corners.map((v) => {return v.rotate(t);});
  corners = corners.map((v) => {return v.add(width/2,height/2);});
  // rawに三角を加えてabs_geq_twoを使う
  const lower = triangulate([...contours, [corners[0].x, corners[0].y, corners[1].x, corners[1].y, corners[2].x, corners[2].y]], {rule:"abs_geq_two", boundaryOnly:true});
  // additiveを逆向きにしてabs_geq_twoを使う
  const upper = triangulate([...contours, [corners[0].x, corners[0].y, corners[2].x, corners[2].y, corners[3].x, corners[3].y]], {rule:"abs_geq_two", boundaryOnly:true});
  shiftContours(lower, 2*cos(t-PI/4), 2*sin(t-PI/4));
  shiftContours(upper, -2*cos(t-PI/4), -2*sin(t-PI/4));
  return [...lower, ...upper];
}

// 最初に重心とって
// あとはそこからの変位でやればいい
// ついでに重心から最も遠い距離取る
// それであのあれ、
// 回転させたときに
// ああ限界をね
// 計算できるわけよ
class Shape{
  constructor(ctr){
    this.v = createVector(random(0.2,0.8)*random([-1,1]), -random(5,10));
    this.a = createVector(0, 0.8);
    this.r = random([-1,1])*random(TAU/240,TAU/160);
    this.alive = true;
    // 先にベクトル化する
    this.ctr = [];
    for(let i=0; i<ctr.length; i+=2){ this.ctr.push(createVector(ctr[i], ctr[i+1])); }
    // 重心計算
    this.g = this.ctr.reduce((p0, p1) => p0.add(p1), createVector(0, 0)); // 2.0.5では()だとバグる
    this.g.div(this.ctr.length);
    // 重心の分だけ引く
    for(const v of this.ctr){ v.sub(this.g); }
    // 半径
    this.radius = 0;
    for(const v of this.ctr){ this.radius = Math.max(this.radius, v.length); }
  }
  update(){
    this.v.add(this.a);
    this.g.add(this.v);
    for(const v of this.ctr){
      v.rotate(this.r);
    }
  }
  display(ctx){
    if(this.g.y - this.radius > height*1.5){
      this.alive = false;
      return;
    }
    ctx.translate(this.g.x, this.g.y);
    drawContoursForVector([this.ctr], ctx);
    ctx.translate(-this.g.x, -this.g.y);
  }
}

結果

ewrwefefef.png

説明

 スライスにはabs_geq_twoを使います。以前説明した方法を使っています。

それで、割った後で若干ずらしています。これをやらないと、くっついてるとみなされ、いくら切ってもくっついたままになってしまうからです。壊した後は、それぞれの図形を重心計算して、重心の周りに回転させながら落としています。以上です。
 なお自作ライブラリのgetTextContoursを使っています。textToContoursが2.0.5で実装されたんですが、ブラウザにより結果が異なるため、はっきり言って、使いたくないからです。

おわりに

 拝読感謝。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?