Archive for February, 2008

Imprinting a Polygon

Saturday, February 9th, 2008

Lets say we have a rectangular mesh, perhaps created with our half mesh tool, and we want to imprint upon it some polygon shape. For instance, we may want to model the temperature or fluid flow in such a polygon and need a mesh in that shape. By imprinting the polygon onto the mesh, we can create a simple stair step mesh that can be used for modeling.

Stair Step Mesh

What is the criteria for determining if a cell is inside the polygon or not? Clearly any cell that is fully inside the polygon would be considered “in”. What about one where one of the vertices is outside? Or two are outside? For the figure above, the imprinting follows a simple rule: if the cell center is in the polygon, then the cell is inside.

Shooting Rays

To create our stair step mesh, we need to cycle through the cells in the rectangular mesh and check each cell center to see if its inside. The classic way to determine if a point lies inside a polygon is to shoot a ray from this point and see how many times it crosses the polygon. If its an even number, the point is outside. An odd number of crossings means its inside. The Wikipedia has a nice article on this here.

Even-Odd Rule

Hitting a vertex – A rare Event?

There is a subtlety with this scheme. Your ray may strike one of the vertices of the polygon. In that case, you will likely get double counting. It will appear that your ray has crossed both polygon segments N and N+1. As a result, you may conclude exactly the wrong result. My initial reaction to this situation was: it could happen, but will be extremely rare. Wrong! Both meshes and polygons are created by humans. And humans tend to like round numbers. It is much more likely you will have a vertex point at (1,1) than at (1.0001, 0.9999). Similarly for the cell positions. In my own testing, I hit this problem right away.

The solution is found in how we think about a segment. Rather than thinking of a segment as two points and the line that connects them, we can think of it as just one point and the line. The figure shows the idea.

Vertex Problem

This can be implemented with a judicious use of the > and >= operators.

Implementations

A quick search of the internet reveals many implementations of this odd-even crossing approach. Bob Stein’s article compares a few implementations, looking at lines of code, number of if statements and so on. The routines he looks at range from 44 to 91 lines of code and include from 2 to 19 if statements, and 0 to 2 divides. When I first implemented this, some years ago, my code ran 70 lines and included 7 if statements and 3 divides. In my defense, my code also checked how close a point was to a segment and included the point if it was within some tolerance. Still, it seems a bit bulky compared to some of the implementations Stein looked at. He was justifiably proud of his implementation, which is only 44 long with no divides.

A bit more looking on the web reveals the incredibly tiny 12 line routine of W. Randolph Franklin. It contains only one if and one divide. I tried it out for a couple of cases and it does seem to capture the essense of the method without a single wasted character. Here is the Franklin’s routine:

int pnpoly(int npol, float *xp, float *yp, float x, float y) { int i, j, c = 0; for (i = 0, j = npol-1; i < npol; j = i++) { if ((((yp[i]<=y) && (y<yp[j])) || ((yp[j]<=y) && (y<yp[i]))) && (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i])) c = !c; } return c; }

(Note this code is Copyright © 1970-2003, Wm. Randolph Franklin )

Stair Step Mesh

With a routine like this, we can easily create a stairstep mesh. You can envision a program something like this:

int main() { MESH_TYPE *pMesh; CELL_TYPE *pCell; int i; //Make a triangle float xp[10] = {0., 1.0, 1.0}; float yp[10] = {0., 0., 1.0}; //Create mesh pMesh = createMesh(0., 0., 1.0, 1.0, 0.1, 0.1); //Determine which cells in mesh are 'live' FOR_EACH(i, pCell, *(pMesh->cells)) { pCell->live = pnpoly(3, xp, yp, pCell->x, pCell->y,) } return 0; }

For this to work, we need to add a few more fields to the cell structure defined previously. Specifically, we need the cell center coordinates and a flag stating whether the cell is live or dead.

Some additional work would be required to actually use this stair step mesh in a CFD code. For instance, it is not efficient to keep dead cells in our cell list. Every time we loop through the cells, we will need an if statement:

if pCell->live …

. Also, we have not identified the boundary faces or made any effort to transfer the boundary conditions from the polygon segments to the edge faces of the stair step mesh. These are tasks for another day.

Half Edge Mesh Implementation

Saturday, February 2nd, 2008

In a previous post, I discussed the elegance of the Half Edge data structure for storing mesh information. This time, we will implement such a scheme in C. The goal is simple: create a program that meshes a 2d rectangle with a structured grid, while storing the information in the Half Edge data structure.

If we know the length and width of the rectangle, and the cell size, we can compute the number of rows and columns in our mesh. We can then loop over these rows and columns, creating cells, faces and vertices and linking them together. As we create these mesh elements, we will store them in lists, using the generalized arrays we developed earlier.

We will build the mesh row by row. The first step is to create the south most row of vertices. As we construct a row of cells, we will be building them on top of the vertices of the previous row. So for the very first row cells, we must first create a row of verts. Once this is done, we enter a pair of nested for loops where we loop over the rows and columns of the mesh. Here is the basic idea

for (i=0; i<numRows; i++) { Prepare the row for (j=0; j<numCols; j++) { Set corner vertices of cell Create new cell Create faces of cell Make remaining connections Hook up the pairs of WF and NF faces Save a few items to help with next cell or next row } wrap up this row }

Once the first row of verts is created, we begin creating cells. For the first cell of a row, we must create a north west vertex. For all other cells on that row, the north east vertex of the previous cell becomes the north west vertex for the new cell. We then need only create one vertex per cell, specially the north east one. This is shown in Step 2 of the figure. With the four vertices of the cell defined, we can then create the four faces of the cell (step 3), then create the cell and link everything together. Its then on to the next cell. In this step by step fashion we build up the mesh.

MeshSteps

A subtlety

There is a subtlety I didn’t mention in the previous post. There are 8 half faces that touch a vertex. How do you know which one to store in the vertex? For an interior vertex it doesn’t really matter, as long as its consistent with the scheme for cycling through the faces around the vertex. Specifically, if you store a face pointing toward the vertex, then you cycle through the faces with the command

face = face->pair->nextF;

If you pick a face that points toward the vertex, then you use

face = face->nextF->pair;

For vertices at the edge of the domain, greater care is needed. Not every face has a pair near the edge, so a command like

face = face->pair->nextF;

can lead to errors. Also, we need to pick the edge that allows us to go fully around the vertex. In the picture below, this can only be satisfied for vertex V by picking face F.

Faces around a vertex

In general, we need to pick the most counter clockwise edge, relative to the vertex. (That is because our circular linked list of faces is organized in a counter clockwise direction.)

More on Lists

In writing this simple mesher, I made use of the generalized arrays developed earlier. In the process of using these, I came across various things that didn’t work that well, so I changed them. For instance, the listAppend function originally returned an index to the data array where the new data was written. It turned out to be more convenient to return a pointer to that data.

I also realized that the creation and destruction functions were overly restrictive. They assume the user has already created the list object and only needs the data array allocated. There are times however, when the user would like both allocated and deallocated for him. So there are now two sets of creation and destruction functions:

void createListData(ALIST *list, long maxEl, size_t elSize) void destroyListData(ALIST *list) void createList(ALIST **list, long maxEl, size_t elSize) void destroyList(ALIST *list)

This provides greater flexibility, but also adds complexity. Speaking of complexity, the astute reader will note in the source code that I did not use these flexible arrays for the lower and oldNF arrays. Instead I used fixed arrays, dimensioned to 1000 elements. A flexible array would be perfect here, since it will auto-expand to however many rows are needed. But the extra hassle didn’t seem worth it for some something so simple.

If I had used these flexible arrays, setting and getting values would have looked like:

LL = *(VERT_TYPE *)getItem(lower, j); setItem(lower, j) = UL;

instead of

LL = lower[j]; lower[j] = UL;

That is a serious loss of readability. When getting the item from the list, you have to remember that getItem returns a pointer to the data element, so you must dereference it. To be proper, you need to cast the pointer first. That makes for some pretty darn ugly code. I could use a macro to clean it up. Perhaps something like:

#define GET_ITEM(pList, index, datatype) *(datatype *)getItem(pList, index);

I will have to experiment with it.

Wrap Up

If you want to play with the code to generate a half edge mesh, you can download it here. Put the files ArrayType.inc nd HalfMesher.c in the same folder and compile and run:

gcc -Wall HalfMesher.c ./a.exe

When main runs, it will call a test function that tests different aspects of a simple 10×10 mesh. It writes out the results to a text file. This is how I made sure the code was relatively bug free.