Polylineのリダクション(Visvalingam-Whyattアルゴリズム)

面積を比較しながら削減していくのでスケールに依存しないという点で便利。

処理の流れ

両隣のポイントと成す三角形の面積を比較して、小さいものから順に削除していく。削除したら前後のポイントの三角形の面積を再計算する。

三角形を視覚化したもの。

コード

初めに初期値として使う各ポイントの三角形の面積を計算する。外積の長さ=ベクトルの成す平行四辺形の面積、を利用する。

// Run Over: Primitives
int pts[] = primpoints(0, @primnum);
for(int i = 1; i < len(pts)-1; i++)
{
    vector prev = point(0, "P", pts[i-1]);
    vector next = point(0, "P", pts[(i+1)%len(pts)]);
    vector pos = point(0, "P", pts[i]);
    
    float area = length(cross(next-pos, prev-pos))/2;
    setpointattrib(0, "area", pts[i], area);
}

このPythonコードではポイントを削除はせず、削除するべきポイントにフラグを振っている。

node = hou.pwd()
geo = node.geometry()

import math

class PT:
    def __init__(self, index, pos, area):
        self.index = index
        self.pos = pos
        self.area = area
        
pts = []
i = 0
#
for pt in geo.points():
    pos = pt.position()
    area = pt.floatAttribValue("area")
    pts.append(PT(i, pos, area))
    i = i+1

# 0-1
percent = `chs("../null1/percent")`
    
# 頂点が3つ以上を条件とする
if(len(pts) > 2):
    
    # 最小頂点を2に設定する(直線)
    num = (int)(len(pts) * percent)
    if(num < 2):
        num = 2
    
    # 削減する目標までループ処理
    while(len(pts) > num):
        # 最大面積を調べる
        maxArea = 0.0
        for i in range(1, len(pts)-1):
            if(pts[i].area > maxArea):
                maxArea = pts[i].area
        #print(maxArea)
    
        # 最小面積のポイントを探す
        minArea = maxArea
        index = -1
        for i in range(1, len(pts)-1):
            if(pts[i].area <= minArea):
                minArea = pts[i].area
                index = i
                
        #print('index:' + str(index))
        prev = index-1
        next = index+1
        
        # 前後の頂点の面積値を更新する
        if(prev > 0):
            p0 = pts[prev-1].pos
            p1 = pts[next].pos
            v0 = p0 - pts[prev].pos
            v1 = p1 - pts[prev].pos
            area = hou.Vector3.cross(v0, v1)
            area = hou.Vector3.length(area)/2
            pts[prev].area = area
        
        if(next < len(pts)-1):
            p0 = pts[prev].pos
            p1 = pts[next+1].pos
            v0 = p0 - pts[next].pos
            v1 = p1 - pts[next].pos
            area = hou.Vector3.cross(v0, v1)
            area = hou.Vector3.length(area)/2
            pts[next].area = area
                
        #print(index)
        pts.pop(index)
    
    result = []
    
    for i in range(0, len(pts)):
        result.append(pts[i].index)
        
    # Detailに情報を保存
    geo.addArrayAttrib(hou.attribType.Global, 'result', hou.attribData.Int, 1)
    geo.setGlobalAttribValue('result', result)

フラグが立っているポイント以外を削除する

// Run Over: Points
int pts[] = detail(0, "result");

if(len(pts) > 0 && find(pts, @ptnum) < 0)
    removepoint(0, @ptnum);
タイトルとURLをコピーしました