ループしているポリラインを隣接するポイントで分割する

環境:Houdini 20.0.751

ポリラインを何かしらで区切るということがよくあるので、今回はポイントを使ってポリラインを区切っていきます。

連環しているところが問題で、0番目のインデックスから処理を始めることができず、最後のインデックスもまたぐため、最初にポリラインを生成する場所を探す必要があります。

ノードネットワーク

128個のポイントで構成された円のポリラインをAdd SOPで配置した4つのポイントで区切っていきます。ポリラインの始点と終点はFuse SOPでつなげておきます。

処理の流れ

ポリライン上のポイントをひとつづつ辿っていき、最初に隣接するポイントがある箇所のインデックスを記録しておきます。

開始するインデックスの場所からポリラインを生成して、次の隣接ポイントの場所をループ処理で探していきます。見つかるまでそのポイントをポリラインに加えていきます。隣接ポイントを見つけたら交点を加えて閉じ、新しくポリラインを生成していく、といったことを繰り返していきます。
線分と点の距離

隣接ポイントが見つかったら交点を加えて新しくポリラインを生成している。

コード

//
// 連環するポリラインを隣接するポイントで区切る
// input0: polyline
// input1: points
// RunOver: Primitives
//

// エッジとの距離の閾値。この値以下なら隣接していると見なす。
float threshold = 1.0;

// 点Pからエッジに最も近い点Cが、そのエッジ上に存在するか
// 存在すれば点Cを返す
function int isClosestPointOnEdge(vector start; vector end; vector point; export vector cross)
{
    int result = 0;
    vector line_vec = normalize(end - start);
    vector point_vec = point - start;
    float d = (dot(point_vec, line_vec));
    
    // 直線の範囲内か判別
    if(d >= 0 && d < length(start - end))
    {
        // 交点座標
        cross = start + line_vec * d;
        result = 1;
    }
    
    return result;
}

// 最初の隣接ポイントがあるポリライン上のポイントインデックスを探す
int startIdx = -1;
vector cross;
int pts[] = primpoints(0, @primnum);
for(int i = 0; i < len(pts)-1; i++)
{
    vector p0 = point(0, "P", pts[i]);
    vector p1 = point(0, "P", pts[i+1]);
    
    int flagExit = 0;
    for(int j = 0; j < npoints(1); j++)
    {
        vector P = point(1, "P", j);
        
        if(isClosestPointOnEdge(p0, p1, P, cross) == 1)
        {
            if(length(cross - P) < threshold)
            {
                startIdx = i;
                flagExit = 1;
                break;
            }
        }
    }
    
    if(flagExit == 1)
        break;
}

// ポリラインを生成していく
if(startIdx != -1)
{
    int prim = addprim(0, "polyline");
    int pt1 = addpoint(0, cross);
    addvertex(0, prim, pt1);
    
    for(int i = 1; i < len(pts); i++)
    {
        // ポイントのインデックスは最初のポイントを加算してシフトする
        int idx = i + startIdx;
        vector p0 = point(0, "P", pts[idx%len(pts)]);
        vector p1 = point(0, "P", pts[(idx+1)%len(pts)]);
        
        // ポイントをポリラインに加える
        addvertex(0, prim, pts[(idx)%len(pts)]);
        
        for(int j = 0; j < npoints(1); j++)
        {
            vector P = point(1, "P", j);
            if(isClosestPointOnEdge(p0, p1, P, cross) == 1)
            {
                // ポイントPが隣接していたら
                if(length(cross - P) < threshold)
                {
                    int pt = addpoint(0, cross);
                    addvertex(0, prim, pt);
                    
                    // 新しくポリラインを生成する
                    prim = addprim(0, "polyline");
                    addvertex(0, prim, pt);
                }
            }
        }
        
        // 最後のエッジ
        if(i == len(pts)-1)
        {
            addvertex(0, prim, pts[(idx+1)%len(pts)]);
            addvertex(0, prim, pt1);
        }
    }
    
    // 元のポリラインを削除
    removeprim(0, @primnum, 1);
}

新しくポリラインを生成しているので元のアトリビュートやグループを維持したい場合はこちらを参考に。
プリミティブのアトリビュートとグループを転写する

タイトルとURLをコピーしました