ジオメトリからポリライン抽出あれこれ  -Various Methods to Extract Polyline from Geometry-

[Houdini Advent Calendar 2020](https://qiita.com/advent-calendar/2020/houdini "Houdini Advent Calendar 2020") 6日目 の記事です

# はじめに
背景TAになって1年目、TAらしい良い記事を書こうとひねり出したテーマは__"ポリラインの抽出"__です
Houdini歴0-2年ぐらいの方に向けて書いたつもりです

ポリラインはポリゴンに比べて単純な構造で、プロシージャルに制御するのにハードルが低いと思います
ジオメトリから欲しいポリラインを適切に抽出できれば、様々なシーンで活躍します
この記事を読んで生成されたポリライン達が、皆さんのHoudiniライフの一助となれば幸いです

また、抽出したポリラインの変換については、↓↓こちらのページにわかりやすくまとまっています
https://issekinichou.wordpress.com/2020/03/01/houdini-curve-editing/

# 動作環境
Houdini Apprentice 18.5.351
Labs 18.5.417

# Primitive毎にエッジからポリラインを抽出
Ends,Primitive,Carve、これら3つのSOPは大体同じことができますが微妙に違います
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/2d41d663-0c33-1dc3-ed34-07b97b2ff33e.png)

### Ends
エッジを一周する一つのポリラインを、プリミティブ毎に生成します
PrimitiveSOPでも同じことができますが、新しそうな__こちらが推奨__です

CloseU>Unroll with New Pointsを選べば端点が繋がっていないポリライン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/652fc764-b337-55d7-2b58-cf5c7fecb043.png)
CloseU>Unroll with Shared Pointsを選べば端点が繋がっていないポリライン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/2f102bf4-b51c-7917-4db2-31035cc35207.png)
を得ることができます

### Primitive
<details><summary>EndsSOPの親玉的SOPで、特別な事情が無い限り基本使わないことを推奨</summary>
恐らくWrangle登場以前の古いノードで、最近のシーンファイルではあまり見かけません(Wrangleでやっちゃうから)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/6ea72bfb-3e06-9e32-5cee-b0ed372798ac.png)
Face/Hull>CloseU のプルダウンから
Unroll with New Pointsを選べば端点が繋がっていないポリライン
Unroll with Shared Pointsを選べば端点が繋がっていないポリライン
をそれぞれ作成することができます(EndsSOPと同じ)
</details>

### 端点について
なお、EndsSOPもPrimitiveSOPも__ポリラインの端点は必ず元のプリミティブの頂点番号0が参照するポイント__になります
任意で指定することはできないので、端点を変更したい場合はReverseSOPのVertex>Shiftで対処してください
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/8ef74763-cdab-01a6-3a32-0b0e926277ef.png)
※PrimitiveSOP内にも同じパラメータがありますがうまく行かないので注意！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/814f4672-feaf-c98b-d5e8-34c57349a831.png)

### Carve
端点をパラメトリック座標から__始点終点を指定して__ポリラインを生成できます
前述の2つのSOPと違い、__必ず端点は繋がってない__状態でポリラインが出力されます
ポリゴンエッジをポリラインにする目的でCarveSOPを使う場合、FirstU,SecondUのみを使用します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/595636bd-690f-4ca5-f412-29222df8ee09.png)
FirstUのみ設定すると、EndsSOPのUnroll with New Pointsと同じ挙動をしますが…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/21ad5933-e369-7123-9fb8-6fac15eebec4.png)
FirstUを設定することで、端点を任意に指定できます(ポイント及びVtxは増えるので注意)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/04da5201-581c-98a2-e21f-57f706b9c6fd.png)
FirstU,SecondUを設定すると、始点終点をそれぞれ指定できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/553ac96c-b2cf-2d32-b59c-ac193a26c22d.png)
※パラメトリック座標についてよく理解していない場合は、以下を参照
プリミティブのパラメトリック空間(暗黙的なUV):　https://www.sidefx.com/ja/docs/houdini/model/primitive_spaces.html
何度読んでも難しい…

### Facet
上記3つのSOPに共通する事項として
入力ポリゴンのエッジを構成するポイントがPrimitive間で共有されていれば、出力されるポリラインもポイントが共有されます
つまりポリラインがダブっている箇所があるのに、ジオメトリとしては切り離せないということになります
基本回避したい状況だと思いますので、FacetSOPを繋いでUniquePointsをチェックしてプリミティブ同士を切り離してから接続するようにしましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/8f5e9a59-a606-7ad4-c365-2e0f1cf0b902.png)
見かけでは分かりづらいので注意！

※ポイントと頂点についてよく理解していない場合は、以下を参照
ポイントと頂点: https://www.sidefx.com/ja/docs/houdini/model/points.html

# 各エッジでダブらないポリラインを抽出
ConvertLine,PolyPathは、上記3つと違ってダブらないポリラインを生成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/18f24db9-65b3-ae0d-c488-af08f4b275de.png)

### ConvertLine
各エッジに一つのポリラインを生成します
入力ジオメトリでポイントが共有されている場合は、ジオメトリとして繋がって出力されます
バラバラのポリラインにしたい場合は、FacetSOPを使用しましょう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/113422a7-4529-7ccd-11cf-89dac188db77.png)
FacetSOPの位置で挙動が変わります
右の結果はEndsSOPなどの出力結果と似ていますが、すべてのエッジが独立したポリラインになっている点で異なります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/482b0332-36f2-b266-392c-4a7d1a379f5b.png)
ちなみにダブルクリックで中を覗けます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/aafe12f9-ab1a-8e8c-4af2-9473bb703dda.png)
結構シンプルな作りでわかりやすいです(こちらのVEX関数については後で説明します)
ポイント番号で駆動しているので、ポリラインの向きの制御が難しいです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/aaa56f8d-b1f8-05ee-50e3-cba24b521656.png)
向きのそろったポリラインが欲しい時は仕方なくVEXを書きます([VEX関数](https://qiita.com/TaroOzaki/items/73d5b3d0d0bc0919828f#vex%E9%96%A2%E6%95%B0)を参照)

### PolyPath
できるだけ繋がったポリラインを生成します
左がConvertLineSOP,右がPolyPathSOPで、プリミティブごとに色分けしています
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/da57693d-3c0e-6dc4-e469-b4a8293ed295.png)
Sweepしたいときに良いです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/0c687629-d01a-8bdd-16a0-384079ff34e9.png)
こちらもダブルクリックで中を覗けます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/4e5a4f99-7166-e0bb-4e8c-23cf53e8a2f8.png)
trace_edgesというAttributeWrangleがキモです…が難しくてよくわかりません
いつか追記するかもしれません

### VEX関数
addprim: https://www.sidefx.com/ja/docs/houdini/vex/functions/addprim.html
addvertex: https://www.sidefx.com/ja/docs/houdini/vex/functions/addvertex.html
の2つでとりあえずポリラインが作れます

↓↓に簡単なサンプルを用意しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/40ee9d0f-eb2d-c32b-e389-bd5aa40e12ef.png)
Wrangleの中身はこんな感じ

```C#:addprim_sample(RunOverはPrimitiveで)
//プリミティブの構成ポイント番号を配列で取得
int primpts[] = primpoints(0,@primnum);

//プリミティブの重心にポイントを作成
int newpt = addpoint(0,@P);

//構成ポイント毎に重心までのポリラインを作成
foreach(int ptnum;primpts){
int polyline = addprim(0,"polyline",newpt);
addvertex(0,polyline,ptnum);
}

//元のポリゴンを消去
removeprim(0,@primnum,1);
```

もっとこの辺りのVEXを詳しく知りたい方は[こちら](https://entagma.com/creating-geometry-with-vex/ "Entagmaさま")や[こちら](https://support.borndigital.co.jp/hc/ja/articles/900003148183-Houdini-%E3%81%A7%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%A8%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96 "BornDigitalさま")などを参照してください

おまけで、Vtxの並び方が綺麗にパラメトリック座標に反映されたポリライン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/4c8065dd-5fe1-bcce-c336-d275e26b2c47.png)

```C#:addprim_eachedge(RunOverはPrimitiveで)
//プリミティブの構成ポイント番号を配列で取得
int primpts[] = primpoints(0,@primnum);


//構成ポイント毎にエッジを生成する
foreach(int ptnum;primpts){
int hedge = pointhedge(0, ptnum);
int ptnum_next = hedge_dstpoint(0,hedge);
addprim(0,"polyline",ptnum,ptnum_next);
}

//元のポリゴンを消去
removeprim(0,@primnum,1);
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/05e0071a-54a8-4e18-c471-8f0266e827ed.png)

```C#:addprim_eachPrim(RunOverはPrimitiveで)
//プリミティブの構成ポイント番号を配列で取得
int primpts[] = primpoints(0,@primnum);
//終点用に配列に始点を追加
push(primpts,primpts[0]);

//空のポリラインを作成
int polyline = addprim(0,"polyline");

//ポリラインに頂点を追加
foreach(int ptnum;primpts){
addvertex(0,polyline,ptnum);
}

//元のポリゴンを消去
removeprim(0,@primnum,1);
```


# EdgeGroupから抽出
["エッジは水物"、と先輩が言っている](https://twitter.com/ijiVFX/status/1170628323120037891?s=20)ように
エッジグループはとても壊れやすく扱いにくい存在で、スプレッドシートでも通常確認することができない困ったちゃんです
しかしながら、エッジからカーブを抽出できるととても重用します
### EdgeGrouptoCurves,Dissolve
端点の結線を任意で指定できる
処理は遅め
Thredsholdがついていない
### PolyExtrude
処理は速い
端点は必ず繋がってしまう
ノード増える
### VEXで
[expandedgegroup](https://www.sidefx.com/ja/docs/houdini/vex/functions/expandedgegroup.html)という関数を使うことで、VEXでエッジグループを扱う事ができました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239573/77f2e46f-b7ce-616d-33e5-d580083ef28d.png)



```C#:ozkEdgeGroupToCurve
//set_detailattrib_exp(RunOver:Detail)
//EdgeGroupを配列にしてDetailアトリビュートに
i[]@exp = expandedgegroup(0,"edgeToCurve");

//create_ptattrib_target(RunOver:Points)
//空の配列アトリビュートを用意
i[]@__target;

//append_ptattrib_target(RunOver:Detail)
//エッジの始点の配列アトリビュートにエッジの終点番号を追加
int exp[] = detail(0,"exp");
int exp_length = len(exp);
for(int i=0; i<exp_length; i+=2){
	setpointattrib(0,"__target",exp[i],exp[i+1],"append");
}

//create_polyline(RunOver:Points)
//配列アトリビュートを元にポリラインを作成
int num_target = len(i[]@__target);

if(num_target!=0){
    foreach(int target;i@__target){
      	int addedprim = addprim(0,"polyline",@ptnum);
        addvertex(0,addedprim,target);
    }
}
```

--------
以下随時追記予定…
--------

# ジオメトリを切断して抽出
### Boolean,Surfsect,Profile
### Clip,VoronoiFracture
### PolyCut



# グリッド構造からアイソカーブを抽出
### Convert
### VEX

# Sim用のSOPを使って抽出
### ConnectAdjecentPieces
### TetConform(旧SolidConform)
### TetEmbed(旧SolidEmbed)


# Labsのツール
### StraightSkelton2D
### StraightSkelton3D
### ExtractBorders
### ExtractSilhouette


# 最短距離,測地線
### FindShortestPath
### DistanceAlongGeometry

# おまけ
### Trail
### DrawCurve