Hilbert Curve

The goal of this assignment was to use Python classes to write scripts to generate a Hilbert curve that can be implemented into Maya.

Initial Curve Generation

# This is the formula that generates the CVs for the Hilbert pattern.
# Code from Malcolm Kesson - www.fundza.com
import sys, math

def hilbert(x0, y0, xi, xj, yi, yj, n):
       if n <= 0:
               X = x0 + (xi + yi)/2
               Y = y0 + (xj + yj)/2

                # Output the coordinates the cv
                print '%s %s 0' % (X, Y)
                hilbert(x0, y0, yi/2, yj/2, xi/2, xj/2, n - 1)
                hilbert(x0 + xi/2, y0 + xj/2, xi/2, xj/2, yi/2, yj/2, n - 1)
                hilbert(x0 + xi/2 + yi/2, y0 + xj/2 + yj/2, xi/2, xj/2, yi/2, yj/2, n - 1)
                hilbert(x0 + xi/2 + yi, y0 + xj/2 + yj, -yi/2,-yj/2,-xi/2,-xj/2, n - 1)

Python Classes

Next, we created a Python class for the Hilbert function, to which we could then add sub-classes for more functionality.
The first sub-class simply took the X and Y outputs and held them, so they could be used by the next sub-class to pass the information to a RIB file.

def data(self, x, y):

With the class implementation, we can now create "instances" of the hilbert function, passing different arguments to each, generating multiple files.

if __name__ == '__main__':
        hilbert1 = Hilbert(0.0, 0.0, 5.0, 0.0, 0.0, 5.0, 5)

We then made a new file, HilbertRib, that served as another sub-class of Hilbert. This file calls to the base class to generate the data, then processes it into a RIB file.

from HilbertClass import Hilbert

class HilbertRib(Hilbert):
       # This is a class (global) variable
        rootpath = '/home/cfetze20/mount/stuhome/tech312/Hilbert/renderman/ribarchives/hilbert_'

        def __init__(self, x0, y0, xi, xj, yi, yj, n, degree, width):
               # Open a file for writing
               self.fullpath = HilbertRib.rootpath + str(Hilbert.counter) + '.rib'

               # Temporary storage for coordinates
               self.temppath = '/temp/temp.rib'
               self.temp = open(self.temppath, 'w')
               self.line_counter = 0

               #Opening and setting up the final RIB file
                self.rib =open(self.fullpath, 'w')
                self.rib.write('Basis "catmull-rom" 1 "catmull-rom" 1\n')

               # Give the input data to the base class. This will fill up the temp file with xyz data.
               Hilbert.__init__(self, x0, y0, xi, xj, yi, yj, n)

               # Writing initial lines to final RIB
               self.rib.write('Curves "cubic" [%d] "nonperiodic" "P" [' % (self.line_counter + 2))

               # Open temp file again, for reading data
               self.temp = open(self.temppath, 'r')
                lines = self.temp.readlines()

               # Transfer the temp data into final RIB
               for n in range (len(lines)):
                       if n == 0:
                       if n == len(lines) - 1:

                       # Close the file - we're done
                       self.rib.write(']\n "constantwidth" [%1.3f]' % width)

       # We override the implementation of the proc in the base class
       def data(self, x, y):
               self.temp.write('%s %s 0\n' % (x,y))
               self.line_counter += 1


After we loaded the RIB into Maya, Malcolm gave us the "sparky" script, which traces a curve from keyframes 0 to 1, across it's UV's.
Rendering a sequence with this shader gave me the video at the top of the page.


Although I still find classes confusing, this exercise was a good introduction to the concept. I can see how using classes could be beneficial while writing scripts that handle multiple jobs and heavy workloads. Also, the Hilbert pattern was very interesting, and I had fun creating renders with it. I'm enjoying becoming reacquainted with Renderman's power and quality.