neigbour
今回は自分に一番近いポイントの情報を取得する方法を・・・って思ってましたが
NoMoreRetakeの北川さんが
N番目に近いポイントの情報を取得する
という記事を先に出していらしてましたね。
やばいパクったと思われるですが、やり方は別だったので紹介したいと思います。

neighbourを用いて近所関係を知ろう!

◆neighbourのリファレンス
http://www.sidefx.com/docs/houdini15.0/vex/functions/neighbour
…とはいったものの、いかんせんこいつ(neighbour)はひとりではあんまり役に立ちません。
なぜなら大体のポリゴンメッシュの場合、一つのポイントに複数のバーテックスがくっついて隣人A、隣人B、隣人C…とご近所さんがたくさんいるからです。

fig0000

そこで、まず自分には何人の隣人がいるかどうかを調べる必要があります。
ここでneighbourcountの出番です。
こいつは隣人の数を調べることが可能です。(接続しているものに限る)

◆neighbourcountのリファレンス
http://www.sidefx.com/docs/houdini15.0/vex/functions/neighbourcount

fig0001

図では3の値が返ってきます。
さあこれで数がわかりました。で、この値をどうするか。
使い方はいろいろあると思いますが、for文でまわして距離を比較してみましょう。

int nc=neighbourcount(0,@ptnum);
int nb;
int i=0,k=0;
float dist0=0,dist1=0;
while(i<nc){
    nb=neighbour(0,@ptnum,i);
    dist1=distance(@P,point(@OpInput1,"P",nb));
    //shorter
    if(dist0>dist1||i==0){
        dist0=dist1;
        k=i;
    }
    i++;
}
if(nc==0){
    removepoint(geoself(),@ptnum);
}else{
    i@ptnum2=neighbour(0,@ptnum,k);
}

これで、自分に一番近い隣人を見つけることができました。(近所付き合いのない人は消えてもらってます)

distanceを用いてN番目ではなく距離で判定をしよう

neighbourでは接続された同士での処理でした。次は自分から一定距離離れたポイントを接続する処理をしてみましょう。
これは海外の方がつくったもので(またパクリ)、自分以外のすべての頂点との距離を比較して指定した距離(radius)よりも小さいものを接続しています。

そして、接続後は相手がもう一度同じ場所で接続をしないようにブラックリストに追加して、無駄なpolylineを生成しないようにも配慮しています。すばらしい!
https://github.com/diegoinacio/coding/blob/master/houdini/geometry_tools/dihNearestNeighborsConnect.py

◆python

import math

###################
# Node initiation #
###################
node = hou.pwd()
geo = node.geometry()

#############
# Functions #
#############
def makeTemplate():
    '''
    Create node parameters
    '''
    pGroup = hou.ParmTemplateGroup()
    rad = hou.FloatParmTemplate('radius', 'Radius', 1)
    pGroup.append(rad)
    node.setParmTemplateGroup(pGroup)

def distance(p1, p2):
    '''
    Calculates euclidean distance between two points.
    '''
    dx = math.pow(p2[0] - p1[0], 2)
    dy = math.pow(p2[1] - p1[1], 2)
    dz = math.pow(p2[2] - p1[2], 2)
    d = math.sqrt(dx + dy + dz)
    return d

########
# Main #
########
makeTemplate()

points = geo.points()
blackList = {}

radius = node.parm('radius').eval()

for point in points:
    blackList[point] = []

for i in range(len(points)):
    po = points[i].attribValue('P')
    for j in range(len(points)):
        if((j == i) or (j in blackList[points[i]])): continue
        pi = points[j].attribValue('P')
        dist = distance(po, pi)
        if(dist <= radius):
            line = geo.createPolygon()
            line.addVertex(points[i])
            line.addVertex(points[j])
            line.setIsClosed(False)
            blackList[points[j]].append(i)

ただし、これはいつものことですが、pythonだと重いわけです。
でwrangleに書き換えようと考えました。
しかし、変換途中で問題が!
馬鹿な…houdiniのVEXは多次元配列をうけつけないだと…
えーっと

float blacklist[]=array();//1次元

とかはOKみたいだけれども

float blacklist[]=array();//1次元
float blacklist[0][]=array();//2次元

だとエラーになります。(*例外として1次元配列にvectorを入れることは可能です)

くそっ!いろいろ調べたけどまったく方法がわからない!
もう無理やりstringに入れてsplitしてやる!

◆wrangle

string blacklist[]=array();
float radius = 1.0;
//float radius = ch("radius");
                
for(int i=0;i<(npoints(0));i++){
    vector p1 =point(@OpInput1,"P",i);
    for(int j=0;j<(npoints(0));j++){
        string blackArray[] = split(blacklist[i],",");
        int black=0;
        for(int k=0;k<len(blackArray);k++){
            if(blackArray[k]==itoa(j)){
                black=1;
            }
        }        
        if((j != i )&&(black != 1)){
            vector p2=point(@OpInput1,"P",j);
            float dist = distance(p1, p2);
            if(dist <= radius){
                int a = addprim(geoself(), "polyline");
                addvertex(geoself(), a, i);
                addvertex(geoself(), a, j);
                blacklist[j]=concat(itoa(i),",",blacklist[j]);
            }
        }
    }
}

*今回はポイント毎の計算はfor文内で行っているのでpointでなくdetail【1回こっきりループ】で回します
*Houdini14、15の場合はlenでいいですが13では存在しないのでarraylengthを使ってください。
*string型の配列を使う。これは
http://forums.odforce.net/topic/24530-reading-a-detail-attribute-array-of-strings-in-vex/
にあったテクニックで

string foo = "S F E C";
string fooArray[] = split(foo);

以上のようにするとスペース” “でスプリットして配列を取得する事が可能になるのです!
つまり”S F E C”のような文字列をarray()に格納してやると疑似的に二次元配列を利用することができます。
⇒実際には”0,10,15…”みたいな文字列がblacklistのarray()に入っています。(“,”でスプリットしています)
一番上の画像はsphereをscatterで適当に散らしてからwrangleに入れてます。

◆splitのリファレンス
http://www.sidefx.com/docs/houdini15.0/vex/functions/split

とりあえず今回は以上です。

>>一覧にもどる