Another Triangle Mesh Example

The CAMAL tri-delaunay mesher fills a surface with a constrained-Delaunay mesh. It inserts points using an advancing-front approach. 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. This mesher requires a parameterization of the surface in u-v space to work. Also, the surface must not require cracking.

tridel2input.png

Figure 8: Surface Geometry

In Figure 8, there are two loops that describe the boundary. The first loop starts at node 1 and ends at node 36. Nodes are not repeated in the loop. The second loop starts at node 37 and ends at node 46. The ordering of loop nodes is important. The exterior loop has a counter-clockwise order, while any holes have a clockwise order when view from above. In other words, if I stand on the surface so that the surface normal extends from my feet to my head, the surface is always on my left. Starting at node 1, the next node in sequence is node 3, then node 4, etc. If I stand at node 37 with the surface on my left, the next node in sequence is node 39, then node 38, etc.

The CMLSurfEval and CMLSizeEval Objects

The tri-delaunay 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.
  4. compute position from u-v parameters,
  5. compute u-v parameters from position,

It uses CMLSizeEval, as size evaluator class, to return size at a point in either cartesian or parametric coordinates. Both classes will be described in My Own Surface Geometry Evaluator and My Own Size Evaluator below.

    // mesh the surface
  int *tris = NULL;
  int new_points = 0, num_tris = 0;
  MySurfEval geom_eval;
  MySizeEval size_eval;
  CMLTriDelaunay tri_mesher(&geom_eval, &size_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");
  }

    // delete memory allocated in read_loop_file
  delete [] points;
  delete [] loops;
  delete [] loop_sizes;

In this example, 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 CMLTriDelaunay class. The second parameter is a pointer to a CMLSizeEval class. It returns a constant value in this case since the element size does not vary over the surface.

An earlier section of the example program read the boundary loop description from a data file just like in A Triangle Mesh Example, the first triangle meshing example. It is passed to the tri-delaunay mesher with a call to CMLTriDelaunay::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, 36 and 12.

Once the boundary definition is set, delete the points and boundary loops since they are no longer needed.

    // 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 CMLTriDelaunay::generate_mesh() verifies the integrity of the input boundary loops and fills the surface with triangles using a hybrid constrained-Delaunay and advancing-front method. Also, it returns the number of points (new_points) and the number of triangles (num_tris) generated.

    // write output file
  if (ret_value) {
    ret_value = write_tri_file(tri_mesher, new_points, num_tris);
    if (!ret_value) {
      printf("Failed writing tri mesh file\n");
    }
  }

  exit(ret_value ? EXIT_SUCCESS : EXIT_FAILURE);
}

The write_tri_file() routine retrieves the triangle mesh to a file one buffer at a time. The first parameter, tri_mesher, is needed for access to the points and triangles of the mesh.

My Own Surface Geometry Evaluator

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

class MySurfEval : public CMLSurfEval
{
public:
  MySurfEval()
      {
          // bounding box of plate with hole
        surfaceArea = 56;
        boxMin[0] = -4.0; boxMin[1] = 0.0; boxMin[2] = 0.0;
        boxMax[0] =  6.0; boxMax[1] = 8.0; boxMax[2] = 0.0;
      }

The constructor for MySurfEval initializes the surface area and bounding box for this flat plat (see Figure 8.) The bounding box are used to transform points to and from parametric space.

  virtual void move_to_surface(double& x, double& y, double& z)
      {
        if      (x > boxMax[0]) x = boxMax[0];
        else if (x < boxMin[0]) x = boxMin[0];
        
        if      (y > boxMax[1]) y = boxMax[1];
        else if (y < boxMin[1]) y = boxMin[1];
        
        z = 0.0;
      }

The MySurfEval::move_to_surface() for this planar surface moves restricts the point to the bounding box and forces the z-coordinate to be zero.

  virtual bool normal_at(double x, double y, double z, 
                         double& nx, double& ny, double& nz)
      {
        nx = 0.0;
        ny = 0.0;
        nz = 1.0;
        return true; // normal vector has unit length (normalized)
      }

The MySurfEval::normal_at() method returns the surface normal closest to x, y and z. The surface normal is (0, 0, 1) since the surface is in the x-y plane. Since the normal is normalized, the method returns true.

  virtual bool uv_from_position(double x, double y, double z, 
                                double& u, double& v)
      { 
        u = (x - boxMin[0]) / (boxMax[0] - boxMin[0]);
        v = (y - boxMin[1]) / (boxMax[1] - boxMin[1]);
        return true;
      }
  virtual void position_from_uv(double u, double v, 
                                double& x, double& y, double& z)
      {
        x = u * (boxMax[0] - boxMin[0]) + boxMin[0];
        y = v * (boxMax[1] - boxMin[1]) + boxMin[1];
        z = 0;
      }

The MySurfEval::uv_from_position() method transforms cartesian coordinates to parametric coordinates. The u- and v-coordinates are linear interpolations between the minimum and maximum values in the x-y plane. The range of the u-v coordinates is [0, 1]. The MySurfEval::position_from_uv() transforms from u-v space back to cartesian coordiantes.

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.

My Own Size Evaluator

This example requires the simplest of size evaluators. The size is constant everywhere. My subclassed reimplementation of the MySizeEval::size_at_point() returns a constant as shown in the example below.

  virtual bool size_at_point(double x, double y, double z, double& size, int)
      {
        size = 1;
        return true;
      }
  virtual bool size_at_point(double u, double v, double& size, int)
      {
        size = 1;
        return true;
      }

The last parameter in the list is level. It may be used to indicate the number of elements removed from a boundary or level. It is not used in this example, see CMLSizeEval::size_at_point().

Figure 9 shows the triangle mesh for the surface in Figure 8.

tridel2mesh.png

Figure 9: Triangle Mesh Example

Other Examples

  1. A Simple Tetrahedral Mesh Example
  2. A Triangle Mesh Example
  3. An Unstructured Quadrilateral Mesh Example
  4. A Swept Hexahedral Mesh Example
  5. Curve Meshing Examples
  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