ポリラインを選択表示する

環境:Houdini 20.0.751

ポリラインにマウスオーバーすると、選択表示されるようにする。

マウスポインタがポリラインの選択範囲にある場合にGeometryDrawableをポイント座標から生成して描画するようにしている。

import hou
import viewerstate.utils as su

class State(object):
    MSG = "Move the mouse over the geometry."

    def __init__(self, state_name, scene_viewer):
        self.state_name = state_name
        self.scene_viewer = scene_viewer
        self.poly_id = -1
        self.geometry = None

        self.edge_range = 8 # エッジの当たり判定(ピクセル)
        
        # Construct a geometry drawable group
        # エッジ
        line = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Line, "line",
            params = {
                "color1": (0.2,0.8,1.0,1.0),            
                "style": hou.drawableGeometryLineStyle.Plain,
                "line_width": 2 }
        )
        
        # ポイント
        point = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Point, "point",
            params = {
                "num_rings": 2,
                "radius": 4,
                "color1": (0.2,0.8,1.0,1.0),
                "style" : hou.drawableGeometryPointStyle.LinearSquare}
        )
        
        self.poly_guide = hou.GeometryDrawableGroup("poly_guide")

        self.poly_guide.addDrawable( line )
        self.poly_guide.addDrawable( point )
        
    def show(self, visible):
        """ Display or hide drawables.
        """
        self.poly_guide.show(visible)

    def onEnter(self, kwargs):
        """ Assign the geometry to drawabled
        """
        node = kwargs["node"]
        self.geometry = node.geometry()
        self.show(True)

        self.scene_viewer.setPromptMessage( State.MSG )

    def onResume(self, kwargs):
        self.show(True)
        self.scene_viewer.setPromptMessage( State.MSG )

    def onInterrupt(self,kwargs):
        self.show(False)

    def onMouseEvent(self, kwargs):
        # hou.GeometryViewportクラス
        viewport = self.scene_viewer.curViewport()
    
        # ジオメトリとの当たり判定
        ui_event = kwargs["ui_event"]
        (origin, dir) = ui_event.ray()
        
        prim_num = -1
          
        # エッジとマウスポインタの交差判定
        for i in range(len(self.geometry.prims())):
            points = self.geometry.prim(i).points()
            
            # エッジごとの判定処理
            # マウスポインタからの線分と各エッジを最短で結ぶ直線と交点を計算している
            A = origin
            B = origin + dir
            ab = (B-A).normalized()

            for j in range(len(points) - 1):
                C = points[j].position()
                D = points[j+1].position()
                cd = (D-C).normalized()
                ac = (C-A)
                
                d0 = (ab.dot(ac)-cd.dot(ac)*ab.dot(cd))/(1-(ab.dot(cd)*ab.dot(cd)))
                d1 = (ab.dot(ac)*ab.dot(cd)-cd.dot(ac))/(1-(ab.dot(cd)*ab.dot(cd)))
    
                # 線分の範囲内か判別
                if d1 >= 0 and d1 < (D-C).length():
                    c0 = A + ab * d0
                    c1 = C + cd * d1
                    dist = (viewport.mapToScreen(c1) - viewport.mapToScreen(c0)).lengthSquared()
                    
                    if dist < pow(self.edge_range, 2):
                        prim_num = i
                        break
            else:
                continue
            break
            
        # 左ボタンクリック時のイベント
        device = ui_event.device()
        if device.isLeftButton():
            self.log(prim_num)
                
        # update the geometry used by drawables
        if prim_num != -1 and prim_num != self.poly_id:
            self.poly_id = prim_num
    
            # Construct a new geometry
            poly_points = self.geometry.prim(self.poly_id).points()
            poly_geo = hou.Geometry()
            poly = poly_geo.createPolygon(False) # hou.Polygonクラスが生成される

            for i in range(len(poly_points)):
                point = poly_geo.createPoint()
                point.setPosition(poly_points[i].position())
                poly.addVertex(point)

            # update the drawable                
            self.poly_guide.setGeometry(poly_geo)
            self.show(True)
                
        elif prim_num == -1:
            self.poly_id = -1
            self.poly_geo = None            
            self.show(False)

    def onDraw( self, kwargs ):
        """ This callback is used for rendering the drawables
        """
        handle = kwargs["draw_handle"]

        self.poly_guide.draw(handle) 

def createViewerStateTemplate():
    """ Mandatory entry point to create and return the viewer state 
        template to register. """

    state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
    state_label = "ViewerStateTest::1.0"
    state_cat = hou.sopNodeTypeCategory()

    template = hou.ViewerStateTemplate(state_typename, state_label, state_cat)
    template.bindFactory(State)
    template.bindIcon(kwargs["type"].icon())

    return template

マウスオーバーによるポリラインの判定

マウスポインタとポリラインのエッジの判定はこの計算を使っている。
直線同士の距離を求める

ポリラインのポイント数が増えるとかなり重い計算になるので、もっと軽くする仕組みが必要。

多重ループの抜け方

for i in range(len(p0)):
    for j in range(len(p1)):
        if p0[i] > p1[j]:
            break
    else:
        continue
    break

ループがbreakではなく通常に終了した場合はcontinueが実行される。continueは外側のループに対するもので、breakがスキップされる。

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