Generating isolines in houdini

Initially I just want to implement the geometric distance calulation mentioned in this article (https://houdinigubbins.wordpress.com/2017/05/02/geodesics-on-meshes/ ) and create something like tihs:

Geodesic distance from one point

I successfully obtained the geometric distance with some hacks, but found there is no node to create simple isolines. Rohan Dalvi has a video on this topic (https://www.youtube.com/watch?v=r2UNkR1ZNrQ), but that method kinda wierd and slow. So I decide to make more general method, which can be quite handy for tasks such as generating contour lines for height field.

final result

The essensital algorithm behind this is  2D marching square. Frist we reading the values of two adjacent vertices, then subtract them with certain isovalue. If one of them is positive while another is negative, it means the isoline must have passed through this edge. Then we can interpolation the vertex value to find the position with value 0. Connect the 0 point on each edge, we will get the isoline crossing this face.

basic case for triangle face

For procedural workflow, consdiering edge cases is very important. While it’s a good idea to triangulate the input beforhand, sometime non-convex polygon are preferred (as used in previous roof generator). For these concave polygons, there could be more than two 0 points, and the points order is also uncertain. So to prevent wrong connection, I use xyzdist to test whether the midpoint of the isoline is on the face, based on the test result we can correctly sort the point order. In addition, special cases should be considered to prevent error, e.g. the isoline happened to pass through one or more polygon vertices.

Contour based on Polyexpand2D edgedist

A good interface design is also very important for reusing node. The interface provide both max_ count and step_ size to control the isoline density. The offset can control the start value to generate isoline, optionally, you can wrap the offset to create loop animation. The level attribute is an integer of 0-max count, unaffected by wrap option, and isovalue is, well, the isovalue at this isoline.

Parameter interface

Color is from a visualizer that automatically added, using oncreated callback script.

node = kwargs['node']
vis = hou.viewportVisualizers.createVisualizer(hou.viewportVisualizers.type('vis_color'), 
  hou.viewportVisualizerCategory.Node, 
  node)
vis.setName(node.name())
vis.setIsActive(True, None)
  
vis.setParm('colortype', 1)
vis.setParm('attrib', 'isovalue')

A sidenote, I encountered a strange error saying “Houdini HDA was expecting {in parameter file” whenever I dropped a new node into the canvas. I haven’t found any relavent information on google. Turned out it is because i already had a visualizer parameter in the HDA, and whenever the oncreated script are called, it conflict with exsisting HDA parameter. After deleting the duplicate visulizer (it’s hidden by default), everything work fine again.

To generate contour of same density, it’s about ten times faster than using Boolean node with intersection plane. It’s also faster than using trace on image, but i didn’t record the exact number.

performace for contour line generation

The node is avaliable on Orblot for free.