[comp.graphics] C code for NURBS surface - Here it is, have fun

blake@ccwf.cc.utexas.edu (Blake Freeburg) (03/03/91)

	Description:
		This file contains various things needed for the generation of nurbs
	curves and surfaces.  Most of this is a copy of what may be found in the
	gl.h header file

	History
	15  Feb.  1991:  Original revision
**************************************************************************/

#define XYZ 	0x0001		/* 3D control point */
#define XYZW	0x0002		/* rational 4D control point */

typedef struct fvec2d {	double x,y; } 		FVEC2D;
typedef struct fvec3d {	double x,y,z; } 	FVEC3D;
typedef struct fvec4d { double x,y,z,w; }	FVEC4D;



-----------CUT HERE---------------------------CUT HERE---------CUT HERE-----
/************************************************************************	
	
	NURBS surface generator

	Programmer: Blake Freeburg
	Description:  
		A set of functions that will produce a mesh of points given the
	s and t knot vectors, the number of knots, the order of each axis,
	byte steps to take in the array, and the control points, in (x,y,z) or
	(x,y,z,w) format.
***************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include "nurbs.h"

#define TRIMESH		/* turn on triangle generation */

long s_byte_length=0L;/* offset to next control point in s direction */
long t_byte_length=0L;/* offset to next control point in t direction */
long s_knot_count=0L; /* number of knots in knot vector in s directon */
long t_knot_count=0L; /* number of knots in knot vector in t directon */

/************************************************************************* 
	double N(i,k,knot,t)
	long i - the index of the knot vector we are at
	long k - the order of the curve
	double knot[] - the knot vector or non-decreasing values
	double t - the point along the curve from tmin < t < tmax
**************************************************************************/
double N(long i, long k, double knot[], double t)
{
	/* temp variables, removed when finished */
	double num1, num2, den1, den2, val1, val2;
	
	if(k == 1)	
	{
		if((knot[i]<=t)&&(knot[i+1]>t))
		{
			return 1.0;
		}
		else 
		{
			return 0.0;
		}
	}
	else
	{
		num1 = (t-knot[i])*N(i,k-1,knot,t);
		den1 = knot[i+k-1] - knot[i];
		num2 = (knot[i+k] - t)*N(i+1,k-1,knot,t);
		den2 = knot[i+k] - knot[i+1];
	
		if ((num1==0.0)&&(den1==0.0)) val1 = 0.0;
		else val1 = num1/den1;
		if ((num2==0.0)&&(den2==0.0)) val2 = 0.0;
		else val2 = num2/den2;

		return(val1 + val2);
	}
}
/************************************************************************** 
	double S(i,j,s_order,t_order,s_count,t_count,sknot,tknot,ctrlarray,s,t);
	long i - the index of the control point in the mesh
	long j - the index of the control point in the mesh 
	long s_order - the order of the curve in s space
	long t_order - the order of the curve in t space
	long s_count - the number of control points in s space
	long t_count - the number of control points in t space
	double sknot[] - the knot vector of non-decreasing values in s space
	double tknot[] - the knot vector of non-decreasing values in t space
	double *ctrlarray - array of data points to be used 
	double s - the point along the surface from umin < u < umax
	double t - the point along the surface from wmin < w < wmax
***************************************************************************/
double S(long i,long j,long s_order,long t_order,long s_count,long t_count, 
	double sknot[],double tknot[],double *ctrlarray, double s,double t)
{
	long x, y;
	double den=0.0, num=0.0;
	char *position;

	/* pointer to find values in array */
	position = (char *)ctrlarray;

	/* compute the numerator first */
	num = (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).w 
			*N(i,s_order,sknot,s)*M(j,t_order,tknot,t);

	/* now compute the denominator */
	for(x=0; x<=(s_count-s_order); x++)
		for(y=0; y<=(t_count-t_order); y++)
			den+= (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).w
					*N(i,s_order,sknot,s)*M(j,t_order,tknot,t);

	if((num == 0.0) && (den == 0.0)) return(0.0);
	else return (num/den);
}
/*************************************************************************
	void precomputebasis(order,x,count,knot,basis)

	long order			the order of the curve in the s direction
	double x				where we are along the knot vector
	long count			the length of the knot vector
	double knot[]		the knot vector itself
	double basis[] 	the array of precomputed knot vectors...
**************************************************************************/
void precomputebasis(long order,double x,long count,double knot[],double basis[])
{
	int i;
	for(i=0;i<=count;i++)
	{
		basis[i] = N(i,order,knot,x);
	}
}
/*************************************************************************
	void sumbasis(position, s_basis, t_basis, s_count, t_count, sum)
	char *position			where we are in the mesh
	double s_basis[]		array of s precomputed mesh values
	double t_basis[]		array of t precomputed mesh values
	long s_count[]			points in s direction
	long t_count[]			points in t direction
	double *sum				the sum of the denominator;
**************************************************************************/
void sumbasis(char *position, double s_basis[], double t_basis[], long s_count, long t_count, double *sum)
{
	long i,j;

	*sum = 0.0;
	for(i=0;i<s_count;i++)
	{
		if(s_basis[i]!=0.0)
		{
			for(j=0;j<t_count;j++)
			{
				if(t_basis[j]!=0.0)
					*sum += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).w * s_basis[i] * t_basis[j];
			}
		}
	}
}
/*************************************************************************
	void nurbs_surface(s_count, s_order, sknot, t_count, t_order, tknot, 
		s_btye_step, t_byte_step, ctrlarray, type)
	long s_count;			the number of knot points in s direction 
	long s_order;			order of the curve in the s direction
	double sknot[];		the knot vector in s direction 
	long t_count;			the number of knot points in t direction 
	long t_order;			order of the curve in the t direction
	double tknot[];		the knot vector in t direction 
	long s_byte_step;		step in bytes to next s point 
	long t_byte_step;		step in bytes to next t point 
	double *ctrlarray;	pointer to the array of points
	long type;				type of curve: XYZ or XYZW
**************************************************************************/
void nthnurbssurface(long s_count, long s_order, double sknot[], long t_count, long t_order, double tknot[],
	 long s_byte_step, long t_byte_step, double *ctrlarray, long type)
{
	int i, j;
	int sd, td;
	double Sknot;
	double s, ds; /* location and step size in the s direction */
	double t, dt; /* location and step size in the t direction */
	double *s_basis; /* precomputed array of basis functions */
	double *t_basis; /* precomputed array of basis functions */
	double SumBasis; /* precomputed basis for point in s,t */
	long resolution_s = 40;
	long resolution_t = 40;
	FVEC3D val;
	char *position;
	double SknotTotal = 0.0;
	XPoints *xlist, *current;
	long xcross_count = 0;
	double leftbound, rightbound;

#ifdef TRIMESH
	/* turn on if you want a triangle mesh to be generated */
	FVEC3D *lastrow;	/* the last row generated.  The first row if none present */
	FVEC3D *thisrow;	/* the current row genrated.  moved to last row when finished */
	long marker;

	lastrow = (FVEC3D *) malloc((resolution_s+1)*sizeof(FVEC3D));
	thisrow = (FVEC3D *) malloc((resolution_s+1)*sizeof(FVEC3D));
	marker = 0;
#endif

	/* allocate the space for the evaluated basis functions */
	s_basis = (double *) malloc((s_count+s_order)*sizeof(double));
	t_basis = (double *) malloc((t_count+t_order)*sizeof(double));

	/* set the position indicator so that we can hop around a bit */
	position = (char *)ctrlarray;

	/* first set up the global step values */
	s_byte_length = s_byte_step;
	t_byte_length = t_byte_step;
	s_knot_count = s_count; 
	t_knot_count = t_count; 


	/* set up the step size and the interval */
	ds = (sknot[s_count] - sknot[0])/(double)(resolution_s);
	s=sknot[0];
	dt = (tknot[t_count] - tknot[0])/(double)(resolution_t);
	t=tknot[0];
	
	for(s=tknot[0];s<=sknot[s_count];s+=ds) 		
	{
		/* precompute the basis functions */
		precomputebasis(s_order,s,(s_count-s_order),sknot,s_basis);

		for(t=tknot[0];t<=tknot[t_count];t+=dt) 				
		{
			val.x = val.y = val.z = 0.00000;
	
			/* precompute the basis functions */
			precomputebasis(t_order,t,(t_count-t_order),tknot,t_basis);
			
			/* check to see if nurbs or bspline curve */
			if(type==XYZW) sumbasis(position, s_basis, t_basis, (s_count-s_order), (t_count-t_order), &SumBasis);
			
			for(i=0;i<(s_count-s_order);i++) 		
			{
				if(s_basis[i]!=0.0)
				{
					for(j=0;j<(t_count-t_order);j++) 	
					{
						if(t_basis[j]!= 0.0)
						{
							if(type == XYZW) /* nonuniform rational surface */
							{
								Sknot = s_basis[i]*t_basis[j]/SumBasis;
								val.x += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).x * Sknot;
								val.y += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).y * Sknot;
								val.z += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).z * Sknot;
							}
							else					/* nonuniform surface - nurbs without the r */
							{
								Sknot = s_basis[i]*t_basis[j];
								val.x += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).x * Sknot;
								val.y += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).y * Sknot;
								val.z += (*(FVEC4D *)(position+i*s_byte_length+j*t_byte_length)).z * Sknot;
							}						
						}
					}		
				}
			}
			/* At this point we have on point generated */
#ifdef TRIMESH
			if(s==0.0) /* if the first in line... */
			{
				/* this should increment counter from 0 to s_count... */
				lastrow[marker].x = val.x;
				lastrow[marker].y = val.y;
				lastrow[marker].z = val.z;
				marker++;
			}
			else
			{
				thisrow[marker].x = val.x;
				thisrow[marker].y = val.y;
				thisrow[marker].z = val.z;
				marker++;	
			}								 
#endif
		}
	
* At this point we have a row of points that have been generated */
#ifdef TRIMESH
		marker = 0;
#endif
#ifdef GL
	/* This didn't work, but it was my first try at using GL trimeshes */
		if(s!=0.0)
		{
			bgntmesh();
				/* the first triangle */
				v3f(&(thisrow[0]));
				v3f(&(lastrow[0]));
				v3f(&(lastrow[1]));
				/* now for the rest of them */
				for(i=1;i<resolution_s;i++)
				{
					v3f(&(thisrow[i]));
					v3f(&(lastrow[i+1]));
				}
				/* end triangle */
				v3f(&(thisrow[i+1]));
			endtmesh();
			free(lastrow);
			lastrow = thisrow;
			thisrow = (FVEC3D *) malloc((resolution_s+1)*sizeof(FVEC3D));
		}
#endif
	}		

}