拡散していくボックス

#はじめに
フジヤマタカシです。まだまだ空きがあるようなので更に投稿させていただきます。
今回はSolverを使ってボックスが広がって何かの形を作るをやってみたいと思います。
１作目のトランスフォーマーに出てきたオールスパークの逆みたいな感じです。

*オールスパークはサイバトロンがメガネに示された地図から必死に探そうとしていたのにセクター7によってとっくに発見されていた例の立方体です。

![ak2c3-ybyu1.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/2f1c4ebb-2dec-37b5-ad06-22aae81f310e.gif)


こういう感じのです。


#拡散
先ずはじめにシンプルにボックスが隣のひとつをボックにしての拡散行く設定です。最初は分かりやすく平面でやってみましょう。ポイント間の間隔は１ユニットです。
![Screenshot from 2018-12-16 13-47-37.png](https://qiita-image-store.s3.amazonaws.com/0/321297/481bf3a3-bed4-3866-13bc-eeba46a662b8.png)


最初にアトリビュート@boxedを準備します。ボックス化しているものは1。そうでなければ0です。１ポイントのみ予め1にしておきます。

```
if(@ptnum == 0)i@boxed = 1;
```
次はSolver内の設定です。

手法としては完成形のポイントを予め用意しておいてSolver内でアトリビュートを書き換えていきます。
このような広がる系の手法だとポイントクラウドで隣接ポイントを判定して自身のアトリビュートを書き換えるのが一般的だと思います。その中でランダムに書き換えたり書き換えなかったり。

今回はボックス化しているポイントがボックス化されてない隣接ポイントの中から１つをボックス化するようにします。この方が拡散の連続性が見れて気持ちの良い展開のようになるからです。

自身のアトリビュートを書き換えるなら、`普通に@Cd = set(1,0,0);`ですが、各ポイントが自身のパラメータを使い別のポイントのアトリビュートを更新するには`setattrib(0,”Cd”, nextpt, set(1,0,0))`を使います。
![Untitled-1.png](https://qiita-image-store.s3.amazonaws.com/0/321297/eb751e69-4b38-0cb8-edc4-2c9fa91759d6.png)
この手法は[EntagmaさんのSpace Colonizationのチュートリアル](https://vimeo.com/231315378)をヒントにしています。やってることは全然違うんですがsetattrib使用のコンセプトはこれを参考にしています。

内部ネットワークは今の所PointWrangleひとつで大丈夫です。

```
int closept[] = pcfind(0, "\@boxed==0", "P", @P, 1, 4);

int selNex = floor( rand(@ptnum * 20 + @Frame) * len(closept) );
int nextP = closept[selNex];

setpointattrib(0, "boxed", nextP, 1);
```

`int closept[] = pcfind(1,"P",@P, 1, 4);`は平面なので周囲最大4つのボックス化されてないポイントを取得しcloseptに配列格納します。

`int selNex = floor( rand(@ptnum * 20 + @Frame) * len(closept) );`は配列の中からひとつを選択するために、ランダムに配列内のインデックスを選びます。

`int nextP = closept[selNex];`でランダムに選んだポイントのナンバーを取得します。

そして`setpointattrib(0, "boxed", nextP, 1);`で選んだポイントのナンバーをboxedアトリビュートを1にします。

Solver後はポイントにboxをコピーしますがその前に@boxed==0のポイントを削除しておきましょう。
色は適当に

```
if(i@boxed==0)removepoint(0,@ptnum);

@Cd = set(rand(@ptnum),0,0);
```

![y6uwv-iob2x.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/e1f130ff-659d-e7f1-9ce8-f9fd59142e21.gif)

#拡散制限
広がり方が均一すぎて微妙ですね。現在@boxedが1であれば毎フレーム周囲のポイントに影響させようとしますがこれに制限をかけます。一度他のポイントをボックス化させると以降のフレームではその操作をしない事にします。その為に@topというアトリビュートを用意しておきます。

そしてこのようにi@top == 1のポイントだけ処理させるようにします。
ボックス化させた隣接ポイントのtopは1になり自身のtopは0になります。

```
int closept[] = pcfind(0,"\@boxed==0","P",@P, 1, 4);

///possibility
int active = 0;
if(len(closept)!=0){
    if(i@top == 1){
        active = 1;
    }
}

i@top = 0;

if (active){
    ///select Next
    int selNex = floor( rand(@ptnum * 20 + @Frame) * len(closept) );
    int nextP = closept[selNex];

    setpointattrib(0, "top", nextP, 1);
    setpointattrib(0, "boxed", nextP, 1);
}
```

![ldp7d-e01z9.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/8325550a-cb9a-ba6f-a90d-6733e4daae10.gif)

袋小路に迷い込み処理が止まってしまいました。。。その為にtopが0の場合でも一定確立で隣をbox化させるに変更します。

```
int closept[] = pcfind(0,"\@boxed==0","P",@P, 1, 4);

///possibility
int active = 0;
if(len(closept)!=0){
    if(i@top == 1){
        active = 1;
    }else{
        if(rand(@ptnum+999 + @Frame) <  0.02 )active = 1;
    }
}

i@top = 0;

if (active){
    ///select Next
    int selNex = floor( rand(@ptnum * 11 + @Frame) * len(closept) );
    int nextP = closept[selNex];

    setpointattrib(0, "top", nextP, 1);
    setpointattrib(0, "boxed", nextP, 1);
}
```

![q5ruy-84oep.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/1914e7ce-b0c2-f415-9f4b-ed430896376c.gif)
とりあえずこれで良しとします。

#拡散時に移動。
次にボックスが隣に移るときに数フレーム移動させたいと思います。その為に３つアトリビュートを追加します。
i@moving ボックスが移動してる間のアトリビュート
f@moveVal ボックスがどのくらい移動してるかのアトリビュート。0スタートでこれが1になるとboxedが1になりmovingは0になる。
v@moveFrom　movingの値を1にしてくれた隣のポイントの位置。

更新用Wrangleのコードはほぼ同じですが、その際にアップデートboxedの代りにmovingを1にします。それと同時に`setpointattrib(0, "moveFrom", nextP, @P); `で位置情報をv@moveFromに渡します。

```
int closept[] = pcfind(0,"\@boxed==0","P",@P, 1, 4);

///possibility
int active = 0;

if(len(closept)!=0){
    if(i@top == 1){
        active = 1;
    }else{
        if(rand(@ptnum+999 + @Frame) <  0.02 )active = 1;
    }
}

i@top = 0;

if (active){
    ///select Next
    int selNex = floor( rand(@ptnum * 11 + @Frame) * len(closept) );
    int nextP = closept[selNex];

    setpointattrib(0, "top", nextP, 1);
    setpointattrib(0, "moving", nextP, 1);
    setpointattrib(0, "moveFrom", nextP, @P); 
}
```

次にもうひとつWrangleを足します。動いてるものに対しての処理なので
Groupは`@moving==1`です。

```
@Cd = set(0,1,0);

float moveStep = 5;

@moveVal += 1.0/moveStep;
if(@moveVal >= 1){
    i@boxed = 1;
    @Cd = set(1,0,0);
    i@moving=0;
}
```
@moveVal += 1.0/moveStep;はmoveStepの値の数値分のステップで移動が完了しbox = 1になります。


Solverの外での設定は@moveValの値でv@moveFrom と@Pをlerpさせて移動させます。

```
if(i@boxed==0 && i@moving == 0)removepoint(0,@ptnum);

@P = lerp(v@moveFrom ,@P ,f@moveVal);
@scale = lerp(set(0.75,0.75,0.75),1,f@moveVal);

@Cd = set(rand(@ptnum),0,0);

if(@top==1)@Cd = 1;
```

![4sdtk-juwxp.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/de43c060-7242-58e7-305f-c02894b01abd.gif)

#立体への適用
立体へのそのまま適用可能です。pcfindのサーチ数を6に変えるだけです。
int closept[] = pcfind(0,"\@boxed==0","P",@P, ch("distance"), 6);
これを作るときのサーチDistanceとポイント間の間隔およびボックスのサイズは揃えておきましょう。

![w35mg-xxiky.gif](https://qiita-image-store.s3.amazonaws.com/0/321297/0b979ba2-4d74-8d0c-9dc7-515608d66541.gif)

作成したファイルはこちらになります。
https://drive.google.com/file/d/1H-UoAgYvWIdAS30UvMWSY4zGUuQTIxZh/view?usp=sharing

では今回は以上になります。ここまで読んでいただきありがとうございます！