A Triangle Mesh Example

The CAMAL tri-advance mesher fills a surface by projecting trangles on the surface begining at the boundary.. The input to the algorithm is a set of boundary points and one or more boundary loops that trace the ordered points on the boundary. In Figure 3, there are two loops that describe the boundary. The first loop starts at node 1 and ends at node 63. Nodes are not repeated in the loop. The second loop starts at node 64 and ends at node 83.

surf1.png

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.

The CMLSurfEval Object

The tri-advance mesher requires additional information describing the surface. The algorithm uses CMLSurfEval, a surface evaluator class, to perform these needed operations such as:

  1. move a point to the surface,
  2. find the normal at a point on the surface,
  3. report various surface characteristics such as area, bounding box, etc.

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.

My Own Surface Geometry Evaluator

The next few code fragments show how to subclass the CMLSurfEval class for this example.

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.

trimesh.png

Figure 4: Triangle Mesh Example

Other Examples

  1. A Simple Tetrahedral Mesh Example
  2. An Unstructured Quadrilateral Mesh Example
  3. A Swept Hexahedral Mesh Example
  4. Curve Meshing Examples
  5. Another Triangle Mesh Example
  6. A Structured Quadrilateral Mesh Example -- Surface Mapper
  7. A Structured Quadrilateral Mesh Example -- Surface Submapper
  8. A Structured Hexahedral Mesh Example -- Volume Mapper

CAMAL 5.2-0 documentation created on 1 Jun 2010
Comments to csimsoft.com