Joshua Stough
The final project.
originally: Nov. 22, 1999
updated (below): Sept. 20, 2001

	My project's purpose was to draw interesting parametrically described 
surfaces in two variables, s and t. You could describe the x y and z coords
as quadratic or cubic functions in s and t. You could then "fly" your view
around the resulting wireframe.  I also implemented a rotation about the coord
axes.  However, the most interesting of the developments, as if drawing and 
manipulating generally described cubics wasn't enough, is the ability to view
and manipulate the tangent plane to the surface, as s and t vary. 
 	Now I'll discuss just how I did it, in short.  There were three parts,
drawing the wire frame given the general quadratic or cubic, the manipulation 
of the wireframe, and the creating and movement of the tangent plane.  First, 
imagine a two dimensional array of vertices.  Each vertex has an x,y,and z 
coord.  If I declared that the above array's size would be 120 (it's a global 
at the top of the header file), that means that there would be 14,400 points 
for the surface held in that array. When the user enters the s and t domains, 
these 14,400 points are spread evenly throughout the surface.  (Recall that x,
y, and z are determined separately as functions of s and t).  An edgelist is 
then created, to give the appearance of a mesh of triangles that describe the 
surface over the domain.
	This last paragraph looks a bit pedantic for its audience. So I'll be 
more brief.  However, by changing the positions of the matrices of 
transformation, I added the "airplane mode," with pannning (or I've seen it 
called strafing) and zoom. On other graphing systems I've seen, it's annoying
how much work it can be to get to the perspective you want.  Thus by allowing 
rotations around the world coord axes (an artifact of hw6), the user's ability 
to manipulate the view of the surface is greatly enhanced.
	The final part, the creation and moving of the tangent plane, was the 
most mathematically trying part of the project.  As a math prof knows, the 
general tangent plane to a surface parametrically described in s and t at a
point x0, y0, z0 is the span of the two vectors, the partial of the surface
with respect to s evaluated at the point, and the partial with respect to t.
This plane is shifted by the x0, y0, z0 vector.  I did this.  Also, for the 
tangent plane to look like a square, I had to orthonormalize the two partials,
using the Gram-Schmidt process.  (The tangent information is stored similarly
to the surface info, as a two dimensional array of points.)
	So there you go.  If I had more time, I would implement functions with
sin and cos.  It really wouldn't take much, just adding a little to determining
a sine function at s and t, and adding to the partials for the tangent. I tried
to write my code as generally as possible, so that such addition functionality
would be easy to implement, with my program as a template.
	As well, as an apology, I have never gotten screean clipping to work as 
well as I would have liked.  So the images can get a little mesy (and wrong) 
when you zoom in too much.

Thank You ,
	Joshua Stough


Sept. 2001 Update:

	There are several things different now.  Let's start with how the surface
is stored.  Still in a 2d array of verticies.  The edgelist is not used now, 
though the program still makes it, since it's just a one-time cost.  The 
surface is now drawn using glVertexArrays.  If we cleverly traverse the 2d
array, we can draw the surface using GL_TRIANGLE_STRIP.  
	This better utilization of gl functionality also mean that the surface 
isn't just wireframe now.  So I've added light sources and a material model
(I did not write it from scratch though, as the rest).  Now the user can 
toggle between a wireframe and smoothly shaded surface.  
	Also, since I had more time (see above), I did implement functions with
sin and cos.  It really didn't take much.  In order to add a new type of 
function to the program, serious modifications need to made in only three 
places.  You must allow the user to enter the coefficients of this new
function (surface::get_xyz()).  You must be able to evaluate your new f(s,t)
at a point (s0, t0) (surface::R (double var[], double &s, double &t).  And 
must be able to determine the tangents and normal of the new type of function
(surface::find_normal(int i,int j,double n[]) and surface::recreate_t_plane()).
Once you've modified the code in these places, the new type of surface 
becomes just as much a part of the program as the others.
	What I'd like to do next with this:  
Currently, x, y, and z must be the same type of function.  I'd like to change
that.
Also, the x, y, and z functions are completely independent of one another, and
only dependent on s and t.  It might be nice to have another independent
variable, say u.  Also, I'd like to introduce interdependencies, so that, for
example, z could be declared as a standard cubic, but with additional terms
of the form (perhaps) f(X(s,t)) + g(Y(s,t)), where f and g are standard cubics
or quadratics or trigonometric, etc, functions. This functionality may have to
be limited though, to avoid structural hazards (to borrow a term from 
computer architecture).
	So, there you go.  Good luck.
	
Joshua Stough 

	


PS:  Here are nice quadratic and cubic functions that I used for testing.

2 (When asked, say you would like quadratic polys.)
X(s,t) = 0 0 0 1 0 0
Y(s,t) = 0 0 0 0 1 0
Z(s,t) = .05 0 .05 0 0 0
domains : -20 20 -20 20
 
 OR

X(s,t) = 0 0 0 1 1 0
Y(s,t) = 0 0 0 1 1 0
Z(s,t) = 0 .11111 0 0 0 0
domain : -15 15 -15 15

 OR

(saddle point)
X(s,t) = 0 .1 0 1 0 0
Y(s,t) = 0 .1 0 0 1 0
Z(s,t) = 0 .1 0 0 0 0
domain : -20 20 -20 20

3 
X(s,t) = 0 0 0 0 0 0 0 1 0 0
Y(s,t) = 0 0 0 0 0 0 0 0 1 0
Z(s,t) = .005 0 0 .005 0 0 0 0 0 0
domain: -15 15 -15 15
 
 OR

(butterfly)
X(s,t) = 0 0 0 0 0 .05 0 0 0 0
Y(s,t) = 0 0 0 0 .05 0 0 0 0 0
Z(s,t) = .005 0 0 .005 0 0 0 0 0 0
domain : -15 15 -15 15 	 

 OR

(trough)
X(s,t) = 0 0 0 0 .05 0 0 0 0 0
Y(s,t) = 0 0 0 0 0 0 0 0 3 0 
Z(s,t) = .005 0 0 .005 0 0 0 0 0 0
domain : -15 15 -15 15

 OR

(windmill)
X(s,t) = -.3333 0 1 0 0 0 0 1 0 0
Y(s,t) = 0 1 0 -.3333 0 0 0 0 1 0
Z(s,t) = 0 0 0 0 1 0 -1 0 0 0
domain : -4 4 -4 4

 OR

X(s,t) = 0 0 0 0 0 0 0 1 0 0
Y(s,t) = 0 0 0 0 0 0 0 0 1 0
Z(s,t) = .02 -.08 0 .02 0 0 0 1 1 -3
domain : -5 5 -5 5
	
0 (the newly added trigonometric functions)
X(s,t) = 1 0 0 0 0 0 0 0 0 0
Y(s,t) = 0 1 0 0 0 0 0 0 0 0
Z(s,t) = 0 0 5 200 -3 200 1 1 -1 1
domain : -50 50 -50 50

OR 

X(s,t) = 1 0 0 0 0 0 0 0 0 0
Y(s,t) = 0 1 0 0 0 0 0 0 0 0
Z(s,t) = 0 0 1 100 1 50 4 4 4 4
domain: -50 50 -50 50

OR

(shield)
X(s,t) = 0 0 0 0 0 0 50 100 0 0
Y(s,t) = 0 0 0 0 0 0 0 0 50 100
Z(s,t) = 1 1 6 600 0 0 0 0 0 0
domain: 0 150 0 150 (or 0 314 0 314 --zero to pi in x and y)

OR

(spade)
X(s,t) = 0 0 0 0 0 0 50 100 0 0
Y(s,t) = 0 0 0 0 0 0 0 0 50 100
Z(s,t) = 0 0 0 0 100 150000 0 0 0 0
domain: 0 628 0 628 (zero to 2*pi)

OR

X(s,t) = 1 0 4 300 0 0 0 0 0 0
Y(s,t) = 0 1 4 300 0 0 0 0 0 0
Z(s,t) = 0 0 5 300 -3 300 0 0 0 0
domain: -100 100 -100 100

OR

X(s,t) = 1 0 0 0 0 0 4 4 0 0
Y(s,t) = 0 1 0 0 0 0 0 0 4 4
Z(s,t) = 0 0 0 0 0 0 4 4 4 4
domain: any square, try -50 50 -50 50