Making a grid flow through guide points using multivariate interpolation with the Python module Numpy / Scipy.
The f@height
attribute is going to be interpolated based on common X and Z positions between the grid and the guide points.
// Grid
f@px = v@P.x;
f@pz = v@P.z;
f@height = 0.0;
// Guide points
f@px = v@P.x;
f@pz = v@P.z;
f@height = v@P.y;
Python's NumPy/SciPy library offers multivariate interpolation. It's making sure the grid will always flow smoothly through our guide points even if they are positioned irregularily.
There are various interpolation methodes provided: multiquadric, inverse multiquadric, gaussian, linear, cubic, quintic and thinplate.
import numpy as np
from scipy.interpolate import griddata
import scipy.interpolate as interp
node = hou.pwd()
geo1 = node.geometry()
inputs = node.inputs()
geo2 = inputs[1].geometry()
method_nr = node.evalParm('method')
method_names = 'multiquadric,inverse_multiquadric,gaussian,linear,cubic,quintic,thin_plate'.split(',')
method_str = method_names[method_nr]
grid_x = np.array(geo1.pointFloatAttribValues('px'))
grid_z = np.array(geo1.pointFloatAttribValues('pz'))
pos_x = np.array(geo2.pointFloatAttribValues('px'))
pos_z = np.array(geo2.pointFloatAttribValues('pz'))
height = np.array(geo2.pointFloatAttribValues('height'))
rbf_height = interp.Rbf(pos_x, pos_z, height, function=method_str)
smooth_rbf_height = rbf_height(grid_x, grid_z)
geo1.setPointFloatAttribValuesFromString("height", smooth_rbf_height.astype(np.float32))
v@P.y = f@height;
The radial basis function (RBF) in Scipy.interpolate does an excellent job at interpolating between irregular points. RBF looks smoother than what we´d get from the Attribute Transfer SOP, too.
Adrian Pan contributed a corresponding example file that works directly with heightfields.
rbf.hip contains a simplified VEX version of multivariate interpolation using radial basis functions. The screenshot compares the attribute transfer node's result (left) with RBF.
int mode = chi('mode');
float k = chf('radius');
float w = 0.0;
vector clr = 0.0;
for(int i = 0; i < npoints(1); i++){
vector pos = point(1, 'P', i);
float d = distance2(v@P, pos);
vector clr_pt = point(1, 'Cd', i);
float weight = 0.0;
if (mode == 0) weight = 1.0 / sqrt(d); // inverse multiquadratic
else if(mode == 1) weight = pow(1.0 + (d * d), -0.5) / d; // multiquadric
else if(mode == 2) weight = exp(-d / (2.0 * k * k)); // gaussian
else if(mode == 3) weight = 1.0 / (d * d * d); // cubic
w += weight;
clr += clr_pt * weight;
}
v@Cd = clr / w;