Computational Geometry Final Project
Jonathan Pompa
Project Objectives
The overall objective of this project is to take a
three dimensional wireframe and make it into a three dimensional surface
mesh. The process by which this will be accomplished is:
Read in Frame Data
Determine the mean direction of the plane
Rotate the frame so that its mean is orthogonal to the X-Y plane
Project the frame onto the X-Y plane
Draw a mesh inside the polygon
Move the mesh up inside the original 3D frame
Actual Results
Rotating the Frame
Assuming the data comes as points oriented around the frame in a counter
clock-wise direction, the points are read in from a file which is simply
a list of points. The data is stored in a linked list of structs. Each
node holds an x, y, and z coordinate and a pointer to the next node. The
head holds data but the tail does not. The tail is linked to itself to
provide a stopping condition in a search.
These are VRML representations of the data. They are shown as the original
data (red), as the rotated frame(green), and as the projection on the X-Y
plane(black).
Frame 1
Frame 2
Determine the direction
Traveling around the frame, the vectors from each point to its neighbors
are calculated and used to compute a cross product. The sum of all of the
cross product vectors is kept as the frame is traversed. Since the cross
product vector is orthogonal to the plane containing two vectors, each
vector represents the perpendicular to the plane at a particular point.
Longer vectors carry more weight in the sum because they form longer cross
vectors. This allows an "average" vector orthogonal to the frame to be
found.
The code for this works well and has been written to inclue all points,
wrapping around the head/tail of the list.
Rotation and Projection
The Cosine of an angle is unique between zero and Pi and between zero and
-Pi. Using this fact, the program takes the vector and calculates its angle
relative to the z axis. First, if the z component of the vector is positive,
the program rotates the vector so that it is parallel with the positive
z axis and it rotates it parallel to the negative z axis if the vector
starts pointed downward. This deals with the mirror effect of the cosine
curve.
The program first determines the angle from the vector to the z axis
on the Y-Z plane and finds the sine and cosine of that angle. It then multiplies
the vector of each point in the list by the rotation matrix to align it
with the z axis. The process is repeated for rotation about the Y-axis.
When this is done, the mean normal vector of the frame is parallel to the
Z-axis. The frame can then be projected onto the X-Y plane by ignoring
the Z component of the coordinates. Everything to this point was done in
a program called "3d2d"
Meshing
I originally intended to just copy the "tri.c" code into my program to
do the meshing of the polygon now projected on a plane. This did not prove
practical because the tri code uses a different data structure than I had.
I did not want to go through every function of tri to convert it to my
data structure so I decided to leave it as a separate program. I modified
it to use the incoming data from my 3d2d program and changed it to output
just a list of points and triangles. This output acts as the seed for my
meshing program which stands separately.
The meshing program is called "mesh". It takes the argument "datafile"
as both of the other programs do. They all take care of their file extensions
and name alterations. The drawback of having three programs is having many
datafiles in a directory after they run and having to type three things
to get a result. The advantage is not having to rewrite the tri.c code
and being able to debug code that is approximately 1/3 as long.
The method of meshing is simple. A maximum line length is specified
in the program and it is used to check against every edge of every triangle
of the triangulation. If an edge exceeds the maximum length, a point is
added at the midpoint of the edge. That point is connected to the opposite
vertex of the triangle and the original triangle is broken in two. The
triangles are stored as a linked list so a new one can be added easily.
Each of the new triangles is then checked again for edge lengths. The points
are stored in an array so that they are easy to index. They are referenced
by the triangles by number so that the triangle vertex connections are
independent of the point coordinates. This is done to help with the later
mesh refinement.
This is the series of transformations that occurs to make a mesh.
The Mesh generation algorithm works when the maximum length is large.
I have a logic problem in the code which causes the meshing to become unstable
when long thin triangles are encountered. The size at which this happens
is different for every polygon and is not predictable.
This is something I will have to fix.
Mesh Optimization
This is the "youngest" part of my code and is incorporated into the mesh
program. It takes each point that has been added by the mesh algorithm
and moves it to the geometric average of the distance to all of its neighbors.
This is not a good energy minimization model but it works for a quick test
of the rest of the code. The process is repeated until all of the points
stop moving then the resulting mesh is printed in a VRML file.
This all works well if there were no boundary points added during the
meshing. The program recognizes the original points of the frame as immobile
but it thinks all added points can be moved in relation to their neighbors.
When an edge is too long, the meshing program adds a new point to the boundary,
preserving the shape but creating smaller elements. This makes the boundary
mobile and leads to some problems with mesh optimization.
No edge points were added here.
Oops! Edge points should be stationary.
The problem will be easy to fix by adding another element to the points
data structure. This will be a flag identifying points that are on the
boundary and excluding them from the optimization process. The points can
be found easily because they all are on lines that are included in only
one triangle. Interior points are included in lines that belong to two
triangles.
Progress from Tuesday to Thursday
I have fixed these problems. The boundary points are immobile as the mesh
is generated and the mesh is now built by maximum area instead of maximum
length. This seems to eliminate the long skinny triangles. I have also
gone to an iterative point adding process where the program adds points,
smooths the mesh and then checks again to see if it needs to add points.
The mesh is not pretty, but it turns out reasonably smooth for a first
try. Here are some examples in 2D.
These are much better than before and the mesh size can be arbitrarily
small.
I can now work in 3D and here are some examples. They look like
minimum area skinning of a surface.
3D Plane
3D Pyramid
3D Notch
Running the Program
I'm sorry this is not all one program, but I did not leave myself
time to convert the code in "tri.c" to my data structure and incorporate
it into the program. The process of meshing this thing is accomplished
by running three programs in series.
Requirements are a data file with a list (in three dimensional
coordinates) of points around a frame, and the programs 3d2d, tri, and
mesh. The mesh size can be adjusted by changing the value of MAXAREA in
the mesh program.
To run this type:
./3d2da "datafile"
./tri "datafile"
./mesh "datafile"
The output can be seen as a VRML file called "datafilemesh.wrl"
email jp8p+@andrew.cmu.edu