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.
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.
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.
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.
Figure 9: Triangle Mesh Example
CAMAL 5.2-0 documentation created on 1 Jun 2010
Comments to csimsoft.com