Point on Polyline Tool
環境:Houdini 20.0.751

ポリライン上にポイントを打っていくツール。
Viewer Stateのコード
#
# Point on Polyline Tool 20240318
#
import hou
import viewerstate.utils as su
from enum import Enum
import math
# Enumでモード切り替え
class Mode(Enum):
SELECT = 0
EDIT = 1
class State(object):
def __init__(self, state_name, scene_viewer):
self.state_name = state_name
self.scene_viewer = scene_viewer
# UIのテンプレート
HUD_TEMPLATE = {
"title": "Point on Polyline", "desc": "tool", "icon": "SOP_subnet",
"rows": [
{"id": "mode", "label": "Mode", "value": "SELECT"},
{"id": "mode_g", "type": "choicegraph", "count": 2, "value": 0},
{"id": "switch_act", "label": "Switch Mode", "key": "Ctrl"},
{"id": "add_act", "label": "Select Prim", "key": "LMB"},
{"id": "focus_act", "label": "Camera Focus", "key": "f"},
]
}
# テンプレートを使用してHUDを設定します。通常では、これはステートが有効な間に一回実行します。
self.scene_viewer.hudInfo(hud_template=HUD_TEMPLATE)
self.mode = Mode.SELECT
self.origin = None
self.direction = None
self.viewport = None
self.selected_point = -1 # 選択中のポイントインデックス(Multiparm順の)
self.selected_prim = -1 # 選択中の親プリミティブのインデックス
self.intersect_prim = -1 # 交差中の親プリミティブのインデックス
self.node = None
self.drawable_geometry = None # 入力されたポリラインのジオメトリ(表示用)
self.src_curve_geometry = None # 入力されたポリラインのジオメトリ
self.src_curve_bsphere = None # 入力されたポリラインのバウンディングスフィア
# 入力用のフラグ
self.mouse_pressed = False
self.old_mouse_pressed = False
self.drag = False # ドラッグ中のフラグ
# ポイントとエッジの判定距離(ピクセル)
self.point_range = 16.0
self.edge_range = 8.0
self.edge_selecting_range = 96.0
# ポイント
drawable_edit_point = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Point, "point",
params = {
"num_rings": 2,
"radius": 3,
"color1": (1.0,0.7,0.1,1.0),
"style" : hou.drawableGeometryPointStyle.LinearSquare}
)
self.guideEditPoint = hou.GeometryDrawableGroup("poly_edit_point")
self.guideEditPoint.addDrawable(drawable_edit_point)
# ポイント(選択中:オレンジ)
drawable_selected_point = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Point, "point",
params = {
"num_rings": 2,
"radius": 5,
"color1": (1.0,0.6,0.1,1.0),
"style" : hou.drawableGeometryPointStyle.LinearSquare}
)
self.guideSelectedPoint = hou.GeometryDrawableGroup("guideSelectedPoint")
self.guideSelectedPoint.addDrawable(drawable_selected_point)
# エッジ(点線)
drawable_selecting_line = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Line, "line",
params = {
"color1": (1.0,0.7,0.1,1.0),
"style": hou.drawableGeometryLineStyle.Dash1,
"line_width": 3 }
)
self.guideSelectingLine = hou.GeometryDrawableGroup("poly_selecting_line")
self.guideSelectingLine.addDrawable(drawable_selecting_line)
# エッジ(実線)
drawable_selected_line = hou.GeometryDrawable(self.scene_viewer, hou.drawableGeometryType.Line, "line",
params = {
"color1": (1.0,0.6,0.1,1.0),
"style": hou.drawableGeometryLineStyle.Plain,
"line_width": 3 }
)
self.guideSelectedLine = hou.GeometryDrawableGroup("poly_selectied")
self.guideSelectedLine.addDrawable(drawable_selected_line)
#
# ユーザーがStateに入ると呼び出される
#
def onEnter(self, kwargs):
self.node = kwargs["node"]
# ガイドのジオメトリ
hda_node = self.scene_viewer.currentNode()
hda_node.cook(force=True) # 強制的にクック
if hda_node:
hda_node.node('geo_src_curve').cook(force=True)
self.src_curve_geometry = hda_node.node('geo_src_curve').geometry()
hda_node.node('src_bsphere').cook(force=True)
self.src_curve_bsphere = hda_node.node('src_bsphere').geometry()
# オーバーレイで描画する入力ポリライン
hda_node.node('geo_src_curve').cook(force=True)
colli = hda_node.node('geo_src_curve').geometry()
hda_node.node('geo_draw').cook(force=True)
geo_draw = hda_node.node('geo_draw').geometry()
self.drawable_geometry = hou.SimpleDrawable(self.scene_viewer, geo_draw, "drawable_geometry")
self.drawable_geometry.setDisplayMode(hou.drawableDisplayMode.WireframeMode)
#self.drawable_geometry.setDisplayMode(hou.drawableDisplayMode.CurrentViewportMode)
self.drawable_geometry.enable(True)
self.drawable_geometry.show(True)
self.node.parm("onEnter").set(1)
# Stateが中断された時に呼び出される
def onInterrupt(self,kwargs):
#self.showGeometry(False)
#self.guideEditPoint.show(False)
#self.guideSelectedPoint.show(False)
self.guideSelectingLine.show(False)
self.guideSelectedLine.show(False)
# Stateの中断が再開された時に呼び出される
def onResume(self, kwargs):
self.drawable_geometry.show(True)
# stateを終了させた時に呼び出される
def onExit(self, kwargs):
self.node.parm("onEnter").set(0)
# マウス左ボタンが押された時に呼ばれる処理
def start(self):
if not self.mouse_pressed:
self.scene_viewer.beginStateUndo("Mouse Pressed")
self.mouse_pressed = True
# マウス左ボタンが放された時に呼ばれる処理
def finish(self):
# 前フレームで押下されていたら
if self.mouse_pressed:
self.scene_viewer.endStateUndo()
self.mouse_pressed = False
# 選択中ラインDrawableの更新処理
def updateGuideSelectingLine(self):
poly_geo = hou.Geometry()
if self.intersect_prim != -1:
poly_points = self.src_curve_geometry.prim(self.intersect_prim).points()
# Construct a new 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)
self.guideSelectingLine.setGeometry(poly_geo) # update the drawable
# 選択済みラインDrawableの更新処理
def updateGuideSelectedLine(self):
poly_geo = hou.Geometry()
#self.log(self.selected_prim)
if self.selected_prim != -1:
poly_points = self.src_curve_geometry.prim(self.selected_prim).points()
# Construct a new 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)
self.guideSelectedLine.setGeometry(poly_geo)
# 編集ポイントdrawableの更新処理(ポイントはUIパラメータから再構成する)
def updateGuideEditPoint(self):
# UIのジオメトリを描画する
poly_geo = hou.Geometry()
poly = poly_geo.createPolygon()
id = self.getIdFromParentPrimIndex(self.selected_prim)
index = self.getMultiparmIndexFromId(id)
# Multiparmから点を描画する
multiparm = self.node.parm("points_" + str(index))
for i in range(multiparm.evalAsInt()):
list_pos = self.getPointPositionsFromMultiparm(index)
for j in range(len(list_pos)):
point = poly_geo.createPoint()
point.setPosition(list_pos[j])
poly.addVertex(point)
self.guideEditPoint.setGeometry(poly_geo) # update the drawable
# 選択済みポイントDrawableの更新処理
def updateGuideSelectedPoint(self):
# UIのジオメトリを描画する
poly_geo = hou.Geometry()
if self.selected_point != -1:
poly = poly_geo.createPolygon()
id = self.getIdFromParentPrimIndex(self.selected_prim)
multiparm_prim_index = self.getMultiparmIndexFromId(id)
parent_id = self.node.parm("parent_id_%d" % (multiparm_prim_index)).eval()
parent = self.getParentPrimIndexFromId(parent_id)
param_u = self.node.parm("u_coord_%d_%d" % (multiparm_prim_index, self.selected_point)).eval()
pos = self.calcPosFromParametricUV(self.src_curve_geometry.prim(parent), param_u)
point = poly_geo.createPoint()
point.setPosition(pos)
poly.addVertex(point)
self.guideSelectedPoint.setGeometry(poly_geo) # update the drawable
# モード切替時
def modeChange(self):
self.selected_point = -1
# SELECT MODE
if self.mode == Mode.SELECT:
self.updateGuideSelectingLine()
self.guideSelectingLine.show(True)
self.updateGuideSelectedLine()
self.guideSelectedLine.show(True)
self.guideEditPoint.show(False)
self.guideSelectedPoint.show(False)
HUD_TEMPLATE = {
"title": "Point on Polyline", "desc": "tool", "icon": "SOP_subnet",
"rows": [
{"id": "mode", "label": "Mode", "value": "SELECT"},
{"id": "mode_g", "type": "choicegraph", "count": 2, "value": 0},
{"id": "switch_act", "label": "Switch Mode", "key": "Ctrl"},
{"id": "add_act", "label": "Select Prim", "key": "LMB"},
{"id": "focus_act", "label": "Camera Focus", "key": "f"},
]
}
self.scene_viewer.hudInfo(hud_template=HUD_TEMPLATE)
# EDIT MODE
else:
self.updateGuideEditPoint()
self.guideEditPoint.show(True)
self.guideSelectingLine.show(False)
self.guideSelectedLine.show(False)
self.updateGuideSelectedPoint()
self.guideSelectedPoint.show(True)
HUD_TEMPLATE = {
"title": "Point on Polyline", "desc": "tool", "icon": "SOP_subnet",
"rows": [
{"id": "mode", "label": "Mode", "value": "EDIT"},
{"id": "mode_g", "type": "choicegraph", "count": 2, "value": 1},
{"id": "switch_act", "label": "Switch Mode", "key": "Ctrl"},
{"id": "add_act", "label": "Select/Add Point", "key": "LMB"},
{"id": "add_act", "label": "Delete Point", "key": "Del"},
{"id": "focus_act", "label": "Camera Focus", "key": "f"},
]
}
self.scene_viewer.hudInfo(hud_template=HUD_TEMPLATE)
self.log(self.mode.name)
#
# Multiparmのポイントを描画する
#
def getPointPositionsFromMultiparm(self, index):
list_position = []
multiparm = self.node.parm("points_" + str(index))
id = self.node.parm("parent_id_%d" % (index)).eval()
parent = self.getParentPrimIndexFromId(id)
for i in range(multiparm.evalAsInt()):
if parent != -1:
param_u = self.node.parm("u_coord_%d_%d" % (index, i)).eval()
pos = self.calcPosFromParametricUV(self.src_curve_geometry.prim(parent), param_u)
list_position.append(pos)
return list_position
# IDから親ポリラインのインデックス番号を返す関数
def getParentPrimIndexFromId(self, id):
prim_index = -1
for i in range(len(self.src_curve_geometry.prims())):
id2 = self.src_curve_geometry.prim(i).attribValue("id")
if id == id2:
prim_index = i
break
return prim_index
# 親ポリラインのインデックスからIDを返す関数
def getIdFromParentPrimIndex(self, src_prim_index):
return self.src_curve_geometry.prim(src_prim_index).attribValue("id")
# IDからMultiparmのインデックス番号を返す
def getMultiparmIndexFromId(self, id):
index = -1
for i in range(self.node.parm("prims").evalAsInt()):
parent_id = self.node.parm("parent_id_%d" % i).eval()
if id == parent_id:
index = i
break
return index
#
# マウスポインタとポイントが交差していたらインデックス番号を、していなかったら-1を返す
#
def isIntersectPoints(self, origin, viewport, positions, radius):
closeIndex = -1
closeDist = 1000000
for i in range(len(positions)):
A = origin
P = positions[i]
dist = (viewport.mapToScreen(P) - viewport.mapToScreen(A)).lengthSquared()
if dist < pow(radius, 2) and dist < closeDist:
closeDist = dist
closeIndex = i
return closeIndex
#
# 入力はhou.Primクラスのリスト。交差していたらインデックス番号を、していなかったら-1を返す
#
def isIntersectPolylines(self, origin, direction, viewport, prims, bspheres, radius):
# エッジとマウスポインタの交差判定
closeIndex = -1
closeDist = 1000000
# 球と直線の交差判定
for i in range(len(prims)):
b_radius = bspheres[i].attribValue("radius")
center = hou.Vector3(bspheres[i].attribValue("center"))
p0 = origin - center
p1 = (origin + direction) - center
v = (p1 - p0).normalized()
a = v.x() * v.x() + v.y() * v.y() + v.z() * v.z()
b = 2 * (p0.x() * v.x() + p0.y() * v.y() + p0.z() * v.z())
c = p0.x() * p0.x() + p0.y() * p0.y() + p0.z() * p0.z() - b_radius * b_radius
# 条件をクリアしたら直線と線分の交差判定
if b * b - 4 * a * c >= 0:
list_points = prims[i].points() # プリミティブからポイントを取得
# エッジごとの判定処理
# マウスポインタからの線分と各エッジを最短で結ぶ直線と交点を計算している
A = origin
B = origin + direction
ab = (B-A).normalized()
for j in range(len(list_points) - 1):
C = list_points[j].position()
D = list_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)))
#c0 = A + ab * d0
c0 = A
c1 = hou.Vector3()
if d1 >= 0 and pow(d1, 2) <= (D-C).lengthSquared(): # 線分の範囲内
c1 = C + cd * d1
elif d1 < 0: # 始点より前
c1 = C
elif pow(d1, 2) > (D-C).lengthSquared(): # 終点より後
c1 = D
dist = (viewport.mapToScreen(c1) - viewport.mapToScreen(c0)).lengthSquared()
if dist < pow(radius, 2) and dist < closeDist:
closeIndex = i
break
else:
continue
break
return closeIndex
#
# マウスポインタとポリラインの交差判定からパラメトリックUVとワールド座標を求める
#
def calcParametricPos(self, origin, direction, viewport, prim, radius):
isIntersect = False
closeDist = 1000000
closePos = hou.Vector3()
# 入力ポリラインからポイントを取得
list_points = prim.points()
perimeter = 0 # Polylineの全長
pos = 0 # Polylineでの位置
# エッジごとの判定処理
# マウスポインタからの線分と各エッジを最短で結ぶ直線と交点を計算している
A = origin
B = origin + direction
ab = (B-A).normalized()
for j in range(len(list_points) - 1):
C = list_points[j].position()
D = list_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)))
# 線分の範囲内か判別
c0 = A
c1 = hou.Vector3()
# 線分の範囲内
if d1 >= 0 and pow(d1, 2) <= (D-C).lengthSquared():
c1 = C + cd * d1
# 始点より前
elif d1 < 0:
c1 = C
# 終点より後
elif pow(d1, 2) > (D-C).lengthSquared():
c1 = D
dist = (viewport.mapToScreen(c1) - viewport.mapToScreen(c0)).lengthSquared()
if dist < pow(radius, 2) and dist < closeDist:
closeDist = dist
closePos = c1
pos = perimeter + d1
isIntersect = True
perimeter += (D-C).length()
parametric_u = pos / perimeter
if parametric_u > 1.0:
parametric_u = 1.0
elif parametric_u < 0:
parametric_u = 0.0
return isIntersect, parametric_u, closePos
#
# パラメトリック座標からワールド座標を計算する
#
def calcPosFromParametricUV(self, prim, param_u):
# 入力ポリラインからポイントを取得
list_points = prim.points()
# ポリラインの全長を計算
perimeter = 0
for j in range(len(list_points)-1):
p0 = list_points[j].position()
p1 = list_points[j+1].position()
dist = (p1-p0).length()
perimeter += dist
pos = perimeter * param_u # 位置を計算
result = hou.Vector3()
sum = 0
for j in range(len(list_points)-1):
p0 = list_points[j].position()
p1 = list_points[j+1].position()
dist = (p1-p0).length()
if sum + dist >= pos:
rest = pos - sum
result = p0 + (p1 - p0).normalized() * rest
break
sum += dist
return result
#
# マウスイベント
#
def onMouseEvent(self, kwargs):
ui_event = kwargs["ui_event"]
device = ui_event.device()
self.origin, self.direction = ui_event.ray()
self.viewport = self.scene_viewer.curViewport()
#
# MODE == SELECT
#
if self.mode == Mode.SELECT:
# エッジとマウスポインタの交差判定
intersect_prim = self.isIntersectPolylines(self.origin, self.direction, self.viewport, self.src_curve_geometry.prims(), self.src_curve_bsphere.prims(), self.edge_range)
# 左ボタンクリック
if device.isLeftButton():
# ポリラインと交差してない場合
if intersect_prim == -1:
# 選択しているポリラインがあれば選択解除する
if self.selected_prim != -1:
self.selected_prim = -1
# ポリラインと交差している
else:
# 同じインデックスなら選択解除する
if intersect_prim == self.selected_prim:
self.selected_prim = -1
else:
self.selected_prim = intersect_prim
# 選択しているMultiparmを先頭にする(なければ生成する)
multiparm = self.node.parm("prims")
id = self.getIdFromParentPrimIndex(self.selected_prim)
index = -1
for i in range(multiparm.evalAsInt()):
parent_id = self.node.parm("parent_id_%d" % i).eval()
if id == parent_id:
index = i
break
if index == -1:
# 新しくMultiparm項目をつくる
index = multiparm.evalAsInt()
multiparm.insertMultiParmInstance(index)
self.node.parm("parent_id_%d" % index).set(id) # uuidをセット
hou.ui.setMultiParmTabInEditors(multiparm, index)
else:
hou.ui.setMultiParmTabInEditors(multiparm, index)
# 現在選択してるタブのインデックスを記録する
parm_editor = hou.ui.paneTabOfType(hou.paneTabType.Parm)
selected_tab_index = parm_editor.multiParmTab('prims')
self.node.parm("tab_index").set(selected_tab_index)
# Drawablesジオメトリの更新
if intersect_prim != -1:
self.intersect_prim = intersect_prim
self.updateGuideSelectingLine()
self.guideSelectingLine.show(True)
else:
self.intersect_prim = -1
self.guideSelectingLine.show(False)
self.updateGuideSelectedLine()
self.guideSelectedLine.show(True)
#
# MODE == EDIT
#
else:
# 左ボタンクリック
if device.isLeftButton():
# ドラッグ中
if self.drag == True:
isIntersect, param_u, pos = self.calcParametricPos(self.origin, self.direction, self.viewport, self.src_curve_geometry.prim(self.selected_prim), self.edge_selecting_range)
id = self.getIdFromParentPrimIndex(self.selected_prim)
multiparm_prim_index = self.getMultiparmIndexFromId(id)
self.node.parm("u_coord_%d_%d" % (multiparm_prim_index, self.selected_point)).set(param_u) # 座標をセット
# ドラッグしていない
else:
intersect_point = -1
#
# ポイントとの交差をチェックする
#
list_positions = [] # 座標を格納
list_indices = [] # 親のプリミティブインデックスを格納
# Multiparmから座標リストをつくり交差を調べる
id = self.getIdFromParentPrimIndex(self.selected_prim)
multiparm_prim_index = self.getMultiparmIndexFromId(id)
multiparm = self.node.parm("points_" + str(multiparm_prim_index))
if multiparm.evalAsInt() > 0:
id = self.node.parm("parent_id_%d" % (multiparm_prim_index)).eval()
parent_index = self.getParentPrimIndexFromId(id)
for i in range(multiparm.evalAsInt()):
if parent_index != -1:
param_u = self.node.parm("u_coord_%d_%d" % (multiparm_prim_index, i)).evalAsFloat()
pos = self.calcPosFromParametricUV(self.src_curve_geometry.prim(parent_index), param_u)
list_positions.append(pos)
list_indices.append(i)
intersect_point = self.isIntersectPoints(self.origin, self.viewport, list_positions, self.point_range)
#self.log("intersect_point:" + str(intersect_point))
# ポイントと交差している
if intersect_point != -1:
if intersect_point == self.selected_point:
self.selected_point = -1
else:
self.start()
self.drag = True
# タブを選択状態にする
hou.ui.setMultiParmTabInEditors(multiparm, intersect_point)
self.selected_point = intersect_point
# ポイントと交差していない
else:
# 押下した瞬間
self.start()
if self.mouse_pressed != self.old_mouse_pressed:
# 親のポリラインとの交差していたら新規ポイントを生成する
intersect_src_prim = self.isIntersectPolylines(self.origin, self.direction, self.viewport, self.src_curve_geometry.prims(), self.src_curve_bsphere.prims(), self.edge_range)
if intersect_src_prim == self.selected_prim:
isIntersect, parametric_u, pos = self.calcParametricPos(self.origin, self.direction, self.viewport, self.src_curve_geometry.prim(intersect_src_prim), self.edge_range)
multiparm = self.node.parm("points_" + str(multiparm_prim_index))
index = multiparm.evalAsInt()
multiparm.insertMultiParmInstance(index)
self.node.parm("u_coord_%d_%d" % (multiparm_prim_index, index)).set(parametric_u) # 座標をセット
# タブを選択状態にする
hou.ui.setMultiParmTabInEditors(multiparm, index)
self.selected_point = index
# 選択解除
else:
self.selected_point = -1
else:
self.drag = False
self.finish()
# Drawablesジオメトリの更新
# 選択されている場合
if self.selected_point != -1:
self.updateGuideSelectedPoint()
self.guideSelectedPoint.show(True)
else:
self.guideSelectedPoint.show(False)
# 常時表示する
self.updateGuideEditPoint()
self.guideEditPoint.show(True)
#if device.isMouse():
# print("マウスが画面内にあります")
#else:
# print("マウスは画面外です")
self.old_mouse_pressed = self.mouse_pressed
return True
#
# キーボードイベント
#
def onKeyEvent(self,kwargs):
ui_event = kwargs["ui_event"]
self.key_pressed = ui_event.device().keyString()
#Delキーが押されたら削除する
if self.key_pressed in ("Del"):
if self.mode == Mode.EDIT:
if self.selected_point != -1:
id = self.getIdFromParentPrimIndex(self.selected_prim)
multiparm_prim_index = self.getMultiparmIndexFromId(id)
multiparm = self.node.parm("points_" + str(multiparm_prim_index))
multiparm.removeMultiParmInstance(self.selected_point)
self.selected_point = -1
self.updateGuideSelectedPoint()
self.guideSelectedPoint.show(True)
self.updateGuideEditPoint()
self.guideEditPoint.show(True)
return True
# カメラのピボットを選択しているポイント座標にする
if self.key_pressed in ("f"):
flag = False
centroid = hou.Vector3()
# SELECT MODE
if self.mode == Mode.SELECT:
if self.selected_prim != -1:
points = self.src_curve_geometry.prim(self.selected_prim).points()
centroid = sum((p.position() for p in points), hou.Vector3()) / len(points)
flag = True
# EDIT MODE
else:
if self.selected_point != -1:
id = self.getIdFromParentPrimIndex(self.selected_prim)
multiparm_prim_index = self.getMultiparmIndexFromId(id)
param_u = self.node.parm("u_coord_%d_%d" % (multiparm_prim_index, self.selected_point)).eval()
centroid = self.calcPosFromParametricUV(self.src_curve_geometry.prim(self.selected_prim), param_u)
flag = True
if flag:
viewTransform = self.viewport.viewTransform()
tuples = viewTransform.asTuple()
worldCamPos = hou.Vector3(tuples[12], tuples[13], tuples[14])
# カメラのピボットを変更する
cam = self.viewport.defaultCamera() # hou.GeometryViewportCamera
camPivotPos = hou.Vector3(cam.pivot())
dist = (worldCamPos - camPivotPos).length()
cam.setPivot(centroid)
cam.setTranslation(centroid + hou.Vector3(0, 0, dist))
return True
self.key_pressed = None
return False
# 修飾キーのイベント
def onKeyTransitEvent(self, kwargs):
ui_event = kwargs["ui_event"]
# Ctrlキー
if ui_event.device().isCtrlKey() == 1:
if self.mode == Mode.SELECT:
if self.selected_prim != -1:
self.mode = Mode.EDIT
self.modeChange()
elif self.mode == Mode.EDIT:
self.mode = Mode.SELECT
self.modeChange()
return False
def onDraw( self, kwargs ):
""" This callback is used for rendering the drawables """
handle = kwargs["draw_handle"]
self.guideEditPoint.draw(handle)
self.guideSelectedPoint.draw(handle)
self.guideSelectingLine.draw(handle)
self.guideSelectedLine.draw(handle)
def createViewerStateTemplate():
state_typename = kwargs["type"].definition().sections()["DefaultState"].contents()
state_label = "ViewerStatePointOnPolylineTool::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
Detail WrangleをHDA内に配置する
//
// パラメータからポリラインを生成する(スナップ)
// Run Over: Primitives
//
int isSnap = chi("../snap");
int numprims = `chs("../prims")`;
for(int i = 0; i < numprims; i++)
{
// idからPrimのインデックスを探す
string id = chs(sprintf("../parent_id_%g", i));
int parent_index = -1;
for(int j = 0; j < nprimitives(1); j++)
{
string uuid = prim(1, "id", j);
if(uuid == id)
{
parent_index = j;
break;
}
}
// 親が存在している
if(parent_index != -1)
{
int numPoints = chi(sprintf("../points_%g", i));
if(numPoints > 0)
{
float u_pos = chf(sprintf("../pt_%g_%g", i, 0));
float curveLength = primintrinsic(1, "measuredperimeter", parent_index);
float sum = 0;
float pos = curveLength * u_pos;
int pts[] = primpoints(1, parent_index);
for(int j = 0; j < len(pts)-1; j++)
{
vector p0 = point(1, "P", pts[j]);
vector p1 = point(1, "P", pts[j+1]);
float length = length(p1-p0);
// ポイントのエリアを発見
if(sum <= pos && pos < sum + length)
{
vector newPos = p0;
if(isSnap)
{
float l0 = pos - sum;
float l1 = length(p1 - p0) - l0;
if(l0 > l1)
newPos = p1;
}
else
{
float l0 = pos - sum;
newPos = p0 + normalize(p1 - p0) * l0;
}
int pt = addpoint(0, newPos);
setpointattrib(0, "parent_id", pt, id);
setpointattrib(0, "u_pos", pt, u_pos);
break;
}
// 何もなければ加算
sum += length;
}
}
}
}