SideFX Houdini customization package
git clone https://git.lucas.co/hou-control.git
houdini-agent/hou_geo.py (3.1K)
1 #!/usr/bin/env python3
2 import sys
3 from bridge import run_in_houdini
4
5 def get_grid_spacing():
6 try:
7 code = "hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor).getPrefs()"
8 import ast
9 res = run_in_houdini(code)
10 if "=>" in res:
11 res = res.split("=>", 1)[1].strip()
12 prefs = ast.literal_eval(res)
13 return float(prefs.get('gridxstep', 2.0)), float(prefs.get('gridystep', 1.0))
14 except Exception:
15 return 2.0, 1.0
16
17 def move_node(node_path, x, y):
18 gx, gy = get_grid_spacing()
19 snapped_x = round(float(x) / gx) * gx
20 snapped_y = round(float(y) / gy) * gy
21
22 code = f"""
23 n = hou.node({repr(node_path)})
24 if n:
25 parent = n.parent()
26 target_pos = hou.Vector2({snapped_x}, {snapped_y})
27
28 # Check for conflict
29 for sibling in parent.children():
30 if sibling != n and sibling.position().distanceTo(target_pos) < 0.1:
31 # Shift conflicting neighbor to the right
32 sibling.move(hou.Vector2({gx}, 0))
33
34 n.setPosition(target_pos)
35 """
36 return run_in_houdini(code)
37
38 def create_node(type_name, name=None, parent="/obj", input_node=None):
39 gx, gy = get_grid_spacing()
40 code = f"""
41 p = hou.node('{parent}')
42 n = p.createNode('{type_name}', node_name={repr(name)} if {repr(name)} else None)
43 if {repr(input_node)}:
44 in_node = hou.node({repr(input_node)})
45 if in_node:
46 n.setInput(0, in_node)
47 print(n.path())
48 """
49 res = run_in_houdini(code)
50 # Extract path from stdout or result_repr
51 node_path = res.strip()
52 if "=>" in node_path:
53 node_path = node_path.split("=>", 1)[0].strip()
54 if not node_path.startswith("/"):
55 # fallback
56 node_path = f"{parent}/{name}"
57
58 if input_node:
59 try:
60 res_pos = run_in_houdini(f"print(tuple(hou.node('{input_node}').position()))")
61 pos_str = res_pos.split("=>", 1)[0].strip()
62 import ast
63 ix, iy = ast.literal_eval(pos_str)
64 move_node(node_path, ix, iy - gy)
65 except Exception: pass
66 else:
67 move_node(node_path, 0, 0)
68 return node_path
69
70 def create_color_node(name, parent, input_node, color=(1, 0, 0)):
71 c_path = create_node("color", name, parent, input_node)
72 run_in_houdini(f"hou.node('{c_path}').parm('colorr').set({color[0]})")
73 run_in_houdini(f"hou.node('{c_path}').parm('colorg').set({color[1]})")
74 run_in_houdini(f"hou.node('{c_path}').parm('colorb').set({color[2]})")
75 return c_path
76
77 if __name__ == "__main__":
78 if len(sys.argv) < 2: sys.exit(1)
79 cmd = sys.argv[1]
80 if cmd == "create":
81 print(create_node(sys.argv[2], sys.argv[3] if len(sys.argv) > 3 else None, sys.argv[4] if len(sys.argv) > 4 else "/obj", sys.argv[5] if len(sys.argv) > 5 else None))
82 elif cmd == "move":
83 print(move_node(sys.argv[2], sys.argv[3], sys.argv[4]))
84 elif cmd == "ls":
85 print(run_in_houdini(f"print([n.name() for n in hou.node('{sys.argv[2] if len(sys.argv) > 2 else '/obj'}').children()])"))
86 elif cmd == "delete":
87 print(run_in_houdini(f"n = hou.node('{sys.argv[2]}'); n.destroy() if n else None"))