環境: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がスキップされる。