Created
April 24, 2019 10:04
-
-
Save eliemichel/7bd59adc6d134b602d59861406fb5933 to your computer and use it in GitHub Desktop.
Script of a BezierSpline Houdini Digital Asset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# BezierSpline | |
# (Houdini Digital Asset Module) | |
# Shared under the terms of the MIT License | |
# Copyright (c) 2019 Elie Michel | |
# This is a wip, expect more docstring eventually | |
from __future__ import print_function | |
# Utils | |
def getPoint(coords, idx): | |
"""Extract point coordinates as a list of floats from a coord field""" | |
if type(coords) == str: | |
coords = coords.split() | |
if idx >= 0 and idx < len(coords): | |
pt = coords[idx].split(',') | |
p = [] | |
for c in pt: | |
try: | |
c = float(c) | |
except ValueError as e: | |
c = 0 | |
p.append(c) | |
return hou.Vector3(p) | |
else: | |
return None | |
def getHandleType(types, idx): | |
idx = int(idx/3) | |
if idx >= 0 and idx < len(types): | |
return types[idx] | |
else: | |
return 'f' | |
def getActiveTool(node): | |
parm = node.parm("active_tool") | |
return parm.menuItems()[parm.eval()] | |
# HDA Module | |
def syncCorrectedCoords(node): | |
coords = node.parm("coords").eval().split() | |
node.parm("corrected_coords").set(' '.join(coords[1:-1])) | |
def syncCurrentToCoords(node): | |
coords_parm = node.parm("coords") | |
coords = coords_parm.eval().split() | |
idx = node.parm("current_index").eval() | |
p = hou.Vector3([node.parm("current_pos"+c).eval() for c in 'xyz']) | |
t = getHandleType(node.parm("types").eval(), idx) | |
if idx >= 0 and idx < len(coords): | |
main_idx = int(idx/3)*3+1 | |
before_p = getPoint(coords, main_idx-1) | |
main_p = getPoint(coords, main_idx) | |
after_p = getPoint(coords, main_idx+1) | |
if idx == main_idx: | |
delta = p - main_p | |
main_p = p | |
before_p = before_p + delta | |
after_p = after_p + delta | |
elif idx == main_idx - 1: | |
before_p = p | |
if t == 's': | |
after_p = 2 * main_p - before_p | |
elif t == 'a': | |
d = after_p.distanceTo(main_p) | |
after_p = main_p + d * (main_p - before_p).normalized() | |
elif idx == main_idx + 1: | |
after_p = p | |
if t == 's': | |
before_p = [2 * a - b for a, b in zip(main_p, after_p)] | |
elif t == 'a': | |
d = before_p.distanceTo(main_p) | |
before_p = main_p + d * (main_p - after_p).normalized() | |
coords[main_idx-1] = "{},{},{}".format(*before_p) | |
coords[main_idx+0] = "{},{},{}".format(*main_p) | |
coords[main_idx+1] = "{},{},{}".format(*after_p) | |
coords_parm.set(' '.join(coords)) | |
syncCorrectedCoords(node) | |
def syncCoordsToCurrent(node): | |
coords = node.parm("coords").eval().split() | |
idx = node.parm("current_index").eval() | |
if idx >= 0 and idx < len(coords): | |
pt = coords[idx].split(',') | |
for i,c in enumerate("xyz"): | |
node.parm("current_pos"+c).set(pt[i]) | |
def syncCurrentToTypes(node): | |
types_parm = node.parm("types") | |
types = types_parm.eval() | |
idx = int(node.parm("current_index").eval()/3) | |
if idx >= 0 and idx < len(types): | |
parm = node.parm("current_type") | |
t = parm.menuItems()[parm.eval()] | |
types = types[:idx] + t + types[idx+1:] | |
types_parm.set(types) | |
def syncTypesToCurrent(node): | |
types = node.parm("types").eval() | |
idx = int(node.parm("current_index").eval()/3) | |
if idx >= 0 and idx < len(types): | |
t = types[idx] | |
node.parm("current_type").set(t) | |
def onCurrentIndexChanged(node): | |
syncCoordsToCurrent(node) | |
syncTypesToCurrent(node) | |
def onActiveToolChanged(node): | |
pass | |
# Viewer State | |
class SplineState(object): | |
def __init__(self, state_name, scene_viewer): | |
self.state_name = state_name | |
self.scene_viewer = scene_viewer | |
def onMouseEvent(self, kwargs): | |
ui_event = kwargs["ui_event"] | |
node = kwargs["node"] | |
dev = ui_event.device() | |
reason = ui_event.reason() | |
coords_parm = node.parm("coords") | |
types_parm = node.parm("types") | |
corrected_coords_parm = node.parm("corrected_coords") | |
coords = coords_parm.eval().split() | |
types = types_parm.eval() | |
handles_parm = node.parm("handles") | |
if getActiveTool(node) not in ['add', 'all']: | |
return | |
if dev.isLeftButton(): | |
origin, direction = ui_event.ray() | |
p = origin - (origin.y()/direction.y()) * direction | |
pt = "{},0,{}".format(p.x(), p.z()) | |
if reason == hou.uiEventReason.Start or reason == hou.uiEventReason.Picked: | |
coords.append(pt) | |
coords.append(pt) | |
coords.append(pt) | |
types += 's' | |
elif coords: | |
coords[-1] = pt | |
c = hou.Vector3([float(x) for x in coords[-2].split(',')]) | |
sp = 2 * c - p | |
spt = "{},0,{}".format(sp.x(), sp.z()) | |
coords[-3] = spt | |
coords_parm.set(' '.join(coords)) | |
types_parm.set(types) | |
syncCorrectedCoords(node) | |
def onHandleToState(self, kwargs): | |
node = kwargs["node"] | |
handle_name = kwargs["handle"] | |
parms = kwargs["parms"] | |
tool = getActiveTool(node) | |
if handle_name == "current_translate": | |
if tool in ['edit', 'all']: | |
for c in "xyz": | |
node.parm("current_pos"+c).set(parms["t"+c]) | |
syncCurrentToCoords(node) | |
elif handle_name == "translate_points": | |
if tool in ['add', 'all']: | |
node.parm("coords").set(parms["pointlist"]) | |
syncCorrectedCoords(node) | |
def onStateToHandle(self, kwargs): | |
node = kwargs["node"] | |
handle_name = kwargs["handle"] | |
parms = kwargs["parms"] | |
tool = getActiveTool(node) | |
if handle_name == "current_translate": | |
if tool in ['edit', 'all']: | |
for c in "xyz": | |
parms["t"+c] = node.parm("current_pos"+c).eval() | |
else: | |
parms["tx"] = 9999999.9 # hack deactivation | |
elif handle_name == "translate_points": | |
if tool in ['add', 'all']: | |
parms["pointlist"] = node.parm("coords").eval() | |
else: | |
parms["pointlist"] = '' | |
nodetype = kwargs['type'] | |
def createViewerStateTemplate(): | |
state_name = nodetype.name() + ".pystate" | |
label = nodetype.description() | |
category = nodetype.category() | |
template = hou.ViewerStateTemplate(state_name, label, category) | |
template.bindFactory(SplineState) | |
template.bindHandle("curvepoint", "translate_points") | |
template.bindHandle("translate", "current_translate") | |
return template |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment