Figure 3: Surface Geometry
The ordering of loop nodes is important. If I stand on the outside of the surface at node 1 with the surface on my left, the next node in sequence is node 3, then node 4, etc. If I stand at node 64 with the surface on my left, the next node in sequence is node 65, then node 66, etc. Notice that the two loops have opposite ordering when viewed from above.
It will be described in context with the following C++ example code fragments.
// mesh the surface int *tris = NULL; int new_points = 0, num_tris = 0; MySurfEval geom_eval; CMLTriAdvance tri_mesher(&geom_eval); ret_value = tri_mesher.set_boundary_mesh(num_points, points, num_loops, loop_sizes, loops); if (!ret_value) { printf("Failed setting boundary mesh\n"); }
Notice that geom_eval is declare of class MySurfEval. It is a subclass of CMLSurfEval and will be described below. It is the first parameter in the constructor for the CMLTriAdvance class. The default value for the second parameter (NULL) is acceptable since there is no special sizing function associated with this problem.
An earlier section of the example program read the boundary loop description from a data file similar to the tetrahedra meshing example. It is passed to the tri-advance mesher with a call to CMLTriAdvance::set_boundary_mesh(...). Again, num_points is the number of points in the two boundary loops, and the loop_sizes is an array of length num_loops with the size of each loop , in this case, 63, 20.
// delete memory allocated in read_loop_file
delete [] points;
delete [] loops;
delete [] loop_sizes;
points = NULL;
Delete the boundary loops definition since it is no longer needed. The points pointer will be reused so it is set to NULL.
// generate the mesh
if (ret_value) {
ret_value = tri_mesher.generate_mesh(new_points, num_tris);
if (!ret_value) {
printf("Failed generating mesh\n");
}
}
The CMLTriAdvance::generate_mesh(...) verifies the integrity of the input boundary loops and fills the surface with triangles using an advancing-front method. Also, it returns the number of points (new_points) and the number of triangles (num_tris) generated. Use these numbers to allocate storage space for the mesh when retrieving the mesh.
// allocate memory to accept tri mesh and retrieve it
if (ret_value) {
tris = new int [num_tris * 3];
points = new double [new_points * 3];
if (tris == NULL || points == NULL) {
printf("Failed allocating memory to receive mesh\n");
ret_value = false;
}
}
if (ret_value) {
ret_value = tri_mesher.get_mesh(new_points, points, num_tris, tris);
if (!ret_value) {
printf("Failed reading tri mesh\n");
}
}
The CMLTriAdvance::get_mesh(...) method retrieves the triangle mesh. The points returned include the input points; they appear as the first num_points in the list in the order they were passed to the tri-advance mesher. The triangle connectivity references the spatial points by their position or index in the points array.
MySurfEval::MySurfEval()
{
// radius of spherical surface
const double radius = 10.0;
const double pi = 3.1415926535;
// surface area of hemisphere (larger than actual surface)
surfaceArea = 2.0 * pi * radius * radius;
// bounding box of hemisphere
boxMin[0] = -10.0; boxMin[1] = -1.0; boxMin[2] = -10.0;
boxMax[0] = 10.0; boxMax[1] = 10.0; boxMax[2] = 10.0;
}
The constructor for MySurfEval initializes the surface area and bounding box for this hemispherical surface (see Figure 3.) It has a radius of 10.0. The surface area and bounding box are used to detect breakout in the advancing-front meshing and do not need to be exact. They must be at least as large as the actual values.
void MySurfEval::move_to_surface(double& x, double& y, double& z) { // radius of spherical surface const double radius = 10.0; // put vector through point on surface of sphere double length = sqrt(x * x + y * y + z * z); double ratio = radius/length; x *= ratio; y *= ratio; z *= ratio; }
The MySurfEval::move_to_surface(...) for this hemispherical surface moves the point along a vector from the origin so that it has length equal to the radius.
bool MySurfEval::normal_at(double x, double y, double z, double& nx, double& ny, double& nz) { // normal direction (from center through point) double length = sqrt(x * x + y * y + z * z); nx = x/length; ny = y/length; nz = z/length; // true if normalized return true; }
The MySurfEval::normal_at() method returns the surface normal closest to x, y and z. It normalizes the surface normal by dividing the vector from the origin to the point by its length. Since the normal is normalized, the method returns true.
This is a simple, hard-coded geometry evaluator. It is not very useful for most applications where surface geometry changes from surface to surface and model to model. You may subclass CMLSurfEval to interface to your geometry engine and take advantage of all of its capabilities. For example, your subclassed constructor may take a pointer to the surface representation from your geometry engine and implement the move_to() and normal_at() methods using that pointer.
Figure 4 shows the triangle mesh for the surface in Figure 3.
Figure 4: Triangle Mesh Example
CAMAL 5.2-0 documentation created on 1 Jun 2010
Comments to csimsoft.com