org.jcae.mesh.amibe.algos2d
Class Initial

java.lang.Object
  extended by org.jcae.mesh.amibe.algos2d.Initial

public class Initial
extends java.lang.Object

Performs an initial Delaunay triangulation. This algorithm is invoked to perform the initial triangulation of each CAD patch, after the discretization of edges has been performed. Wires of this patch are processed in turn. All nodes belonging to discretization of these edges are projected onto this 2D patch and collected into a list. These nodes are boundary nodes, and all other nodes will be inserted in the interior domain. A bounding box enclosing all these nodes in the 2D space is computed, and a KdTree instance can then be initialized by Mesh.resetKdTree(double [], double []).

Some checks have been added to remove tiny edges and make sure that boundary is closed. But this is a hack, the right solution is to analyze the overall CAD structure and make sure that edges are well connected and not too small. In particular, the tolerance on vertex location should be used to remove vertices which may be duplicates.

A first triangle is created by iterating over the list of boundary nodes to find three vertices which are not aligned. The outer domain is also triangulated; Mesh.outerVertex is a vertex at infinite, and three outer triangles are created by joining this vertex to vertices of the first triangle. With this trick, there is no need to have special cases when vertices are inserted outside the convex hull of already inserted vertices, and triangle location always succeed. If these outer triangles did not exist, we would have to triangulate the convex hull of nodes.

Boundary nodes are then inserted iteratively. For the moment, an Euclidian 2D metric is used because a 3D metric will not help on a very rough triangulation. The nearest vertex already inserted in the mesh is retrieved with KdTree.getNearestVertex(Mesh, Vertex). It has a reference to a triangle containing this vertex. From this starting point, we search for the Triangle containing this boundary node by looking for adjacent triangles into the right direction. This Triangle is splitted into three triangles (even if the vertex is inserted on an edge), and edges are swapped if they are not Delaunay. (This criterion also applied with our Euclidian 2D metric)

When all boundary nodes are inserted, an unconstrained Delaunay mesh has been built. The list of boundary nodes computed previously gives a list of boundary edges, which needs to be enforced. This is performed by Mesh2D.forceBoundaryEdge(Vertex2D, Vertex2D, int); the segments which intersect the enforced edge are swapped. The AbstractHalfEdge.BOUNDARY attribute is set on these edges (and on matte edges).

We know that the Triangle bound to Mesh.outerVertex is an outer triangle. Triangles adjacent through a boundary edge are interior triangles, and triangles adjacent through non-boundary edges are also outer triangles. All triangles of the mesh are visited, and outer triangles are tagged with the AbstractHalfEdge.OUTER attribute. If an inconsistency is found (for instance a boundary edge seperate two outer triangles), InitialTriangulationException is raised. This means that boundary was invalid, eg. it is not closed or intersects itself. This detection of broken boundaries could be improved by taking advantage of some OpenCascade features, like the detection of self-intersection and object tolerance.

It is important to note that triangles in holes have their OUTER attribute set, but are not linked to Mesh.outerVertex. So this attribute is the only safe way to detect outer triangles. As outer triangles are not removed, vertex location can still be performed as if the domain was convex. All subsequent 2D algorithms should consider these points.

This is very different when remeshing 3D meshes; in such a case, boundary edges are detected because they have only one incident face. An outer triangle is then added by connecting end points to Mesh.outerVertex, but outer triangles are not connected together. Mesh domain is not convex, but that does not matter because 3D algorithms do not require vertex location.

After this initial triangulation has been performed, it is time to add interior vertices to fulfill user's requirements. The Euclidian 2D metric is replaced by the 2D Riemannian metric Metric2D induced by surface local properties and user constraints. But current triangles can cross the whole surface, so metrics of its vertices may be very different. There are two problems:

In order to improve accuracy, Frédéric Hecht advised to recursively split segments when metrics at end points are very different. This has been implemented in Calculus3D.distance(Vertex2D, Vertex2D) but did not give good results. Now that the whole process works much better, this issue could be investigated again.

About inverted triangles, he also explained that we do not have to care if large triangles are inverted in 3D, because they will be fixed naturally when being splitted up.

The current implementation begins with swapping edges (by calling VirtualHalfEdge2D.checkSmallerAndSwap(org.jcae.mesh.amibe.patch.Mesh2D)) if the opposite diagonal is smaller. This method did improve some test cases, but is certainly useless with the current meshing process because it has been dramatically improved since these tests have been performed. The following steps are then performed:

  1. Insert interior nodes (see Insertion) to have a mesh with target size of 16.
  2. Check compatibility between triangle normals and normals to the surface (see ConstraintNormal3D). If triangle inversion gives better result, edges are swapped.
  3. Insert interior nodes to have a mesh with target size of 4.
  4. Check compatibility between triangle normals and normals to the surface.
  5. Insert interior nodes to have a mesh with target size of 1.
  6. Check compatibility between triangle normals and normals to the surface.


Constructor Summary
Initial(Mesh2D m, MeshTraitsBuilder mtb, MMesh1D m1d)
          Creates a Initial instance.
Initial(Mesh2D m, MeshTraitsBuilder mtb, MMesh1D m1d, java.util.Collection<MNode1D> innerNodes)
          A constructor to insert constrain some points in the mesh
Initial(Mesh2D m, MeshTraitsBuilder mtb, Vertex2D[] boundaryNodes, java.util.Collection<MNode1D> innerNodes)
          A constructor for Bora, which use it's own Mesh1D
 
Method Summary
 void compute()
          Launch method to mesh a surface.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

Initial

public Initial(Mesh2D m,
               MeshTraitsBuilder mtb,
               MMesh1D m1d)
Creates a Initial instance.

Parameters:
m - the data structure in which the mesh will be stored.
m1d - 1D mesh

Initial

public Initial(Mesh2D m,
               MeshTraitsBuilder mtb,
               MMesh1D m1d,
               java.util.Collection<MNode1D> innerNodes)
A constructor to insert constrain some points in the mesh


Initial

public Initial(Mesh2D m,
               MeshTraitsBuilder mtb,
               Vertex2D[] boundaryNodes,
               java.util.Collection<MNode1D> innerNodes)
A constructor for Bora, which use it's own Mesh1D

Method Detail

compute

public void compute()
Launch method to mesh a surface.