NURBS - Non Uniform Rational B-Splines.

My son learned about rational polynomials and rational functions recently and I volunteered
to talk about how they are used in the "real world" - in the form of NURBS. Here are some notes
I gathered in preparation. I gave the 20 minute talk on 20-DEC-2000 at Lincoln Sudbury RHS.

ART!!!

Tutorial for Artists.

Rhino's Gallery. lots of cool images made with their NURBS modeller.

Breezy Intro for GEEKS #1 scanned from a world famous PEX book (Hardenbergh)

Breezy Intro for GEEKS #2 CAUTION, some formulas...

Trivial Python NURBCurve evaluation code

In April 2003, I ported my NURBS playground to Python and embedded it in a tiny App I am using to try to do some art like the image on the right. I must warn you that the image on the right did get a bit of processing after the App snapped it.

The source for the application is twist1.py and it is a terrible mess of a UI, but, it has been useful to me. Perhaps you will find it useful. You will also need Point.py
 

This evaluates a 3D NURBCurve


def C(t, order, points, weights, knots):
    c = Point([0,0,0])
    rational = 0
    i = 0
    while i < len(points):
	b = B(i, order, t, knots)
        p = points[i] * (b * weights[i])
        c = c + p
        rational = rational + b*weights[i]
        i = i + 1
    return c * (1.0/rational)


def B(i,k,t,knots):
    ret = 0
    if k>0:
        n1 = (t-knots[i])*B(i,k-1,t,knots)
        d1 = knots[i+k] - knots[i]
        n2 = (knots[i+k+1] - t) * B(i+1,k-1,t,knots)
        d2 = knots[i+k+1] - knots[i+1]
        if d1 > 0.0001 or d1 < -0.0001:
            a = n1 / d1
        else:
            a = 0
	if d2 > 0.0001 or d2 < -0.0001:
            b = n2 / d2
        else:
            b = 0
        ret = a + b
        #print "B i = %d, k = %d, ret = %g, a = %g, b = %g\n"%(i,k,ret,a,b)
    else:
        if knots[i] <= t and t <= knots[i+1]:
            ret = 1
        else:
            ret = 0
    return ret

NURBS code for a circle

Circle Example with OpenGL code included below and ancient PEX C code which takes three XYZ points and put a circle thru them. The example is taken from the book Mathematical Elements for Computer Graphics, by David F. Rogers and J Alan Adams.

Here is a little NURBCurve playground using that same example. It evaluates the curve in a very transparent, if very inefficient way. However, if you are trying to build up your understand of how NURBS work, this bit of source if for you.

There is also a good page based on a talk given to college students by Markus Altmann
 

/*
 * OpenGL code for drawing a circle with a NURBS curve.
 *
 * Method found on page 374, Mathematical Elements for Computer Graphics, 
 *   2nd ed. David F. Rogers and J Alan Adams. McGraw Hill, 1990.
 * This is the triangle method, using 7 control points at the corners
 *   and middles of an equilateral triangle, with knot vector
 *   [ 0, 0, 0, 1, 1, 2, 2, 3, 3, 3 ] 10 knots (nCtrlPoints + order)
 *   [ 1.0, 0.5, 1.0, 0.5, 1.0, 0.5, 1.0] weights, one per ctrlPoint.
 * Control points are the corners and midpoints of an equilateral triangle
 * 0,0 - 1,0 - 0,sqrt(3)/2 except it has been translated to -.5, -.5
 * The first point is repeated as the 7th point.
 * 
 * see picture at http://www.jch.com/NURBS/NURBCirc.gif
 * and other notes at http://www.jch.com/NURBS/
 * YON - Jan C. Hardenbergh
 */


GLUnurbsObj *pNurb = NULL;
int nCtrlPoints = 7;    
GLfloat ctrlPoints[7][4];
GLfloat pointsWeights[7][4]  = {    
    {0,-.5, 0, 1},
    {.5,-.5, 0, 0.5},
    {0.25, -0.0669873, 0, 1},
    {0, 0.3880254, 0, 0.5},
    {-.25,  -0.0669873, 0, 1},
    {-.5,-.5, 0, 0.5},
    {0,-.5, 0, 1}};
    
    
    // Knot vector
    int order = 3;
    int nKnots = 10;
    GLfloat Knots[10] = {0,0,0,1,1,2,2,3,3,3};
#define kStride 4
    
    // Called to draw scene
    void RenderScene(void)
    {
        int i, j;
        
        // Clear the window with current clearing color
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        // Render the NURB
        
        // make sure we are setup
        if (!pNurb)
        {
            // Setup the Nurbs object
            pNurb = gluNewNurbsRenderer();
            gluNurbsProperty(pNurb, GLU_SAMPLING_TOLERANCE, 5.0f);
            //gluNurbsProperty(pNurb, GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON);
            //gluNurbsProperty(pNurb, GLU_DISPLAY_MODE, (GLfloat)GLU_FILL);
            
            // make the NURBS control points from the points & weights
            for(i = 0; i < nCtrlPoints; i++)
            {
                ctrlPoints[i][3] = pointsWeights[i][3];
                for (j = 0; j < 3; j++)
                {
                    ctrlPoints[i][j] = pointsWeights[i][j]*ctrlPoints[i][3];
                }
            }
        }
        
        glColor3f(1,0,1);
        
        // Begin the NURB definition
        gluBeginCurve(pNurb);
        
        // Send the Non Uniform Rational BSpline
        gluNurbsCurve(pNurb, nKnots, Knots, kStride, 
            &ctrlPoints[0][0], order, GL_MAP1_VERTEX_4);
        
        gluEndCurve(pNurb);
        
        // Draw the control points in red
        glPointSize(3.0f);
        glColor3f(1,0,0);
        glBegin(GL_POINTS);
        for(i = 0; i < nCtrlPoints; i++)
            glVertex3fv(pointsWeights[i]);  
        glEnd();
        
        // Flush drawing commands
        glFlush();
    }
    
3-DEC-2000 jch    -  updated 29-MAY-2003